[OSC] Implement Share Group Type Commands

In this patch we add the following openstack share commands:
share group type create
share group type delete
share group type list
share group type set
share group type unset
share group type show
share group type access create
share group type access delete
share group type access list

Partially-implements: bp openstack-client-support

Co-Authored-By: Ashley Rodriguez <ashrodri@redhat.com>
Change-Id: I31b22013b60560c17bc95c41a51efc5e6c7a80ca
This commit is contained in:
Victoria Martinez de la Cruz 2021-08-18 16:45:20 +00:00
parent dcb25e54e9
commit fa8a4b03f8
11 changed files with 1385 additions and 21 deletions

View File

@ -163,4 +163,11 @@ share groups
============ ============
.. autoprogram-cliff:: openstack.share.v2 .. autoprogram-cliff:: openstack.share.v2
:command: share group * :command: share group [!t]*
=================
share group types
=================
.. autoprogram-cliff:: openstack.share.v2
:command: share group type *

View File

@ -70,7 +70,6 @@ class Manager(utils.HookableMixin):
data = data['values'] data = data['values']
except KeyError: except KeyError:
pass pass
with self.completion_cache('human_id', obj_class, mode="w"): with self.completion_cache('human_id', obj_class, mode="w"):
with self.completion_cache('uuid', obj_class, mode="w"): with self.completion_cache('uuid', obj_class, mode="w"):
resource = [obj_class(self, res, loaded=True) resource = [obj_class(self, res, loaded=True)

View File

@ -103,12 +103,18 @@ SNAPSHOT_SUPPORT = 'snapshot_support'
CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = 'create_share_from_snapshot_support' CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = 'create_share_from_snapshot_support'
REVERT_TO_SNAPSHOT_SUPPORT = 'revert_to_snapshot_support' REVERT_TO_SNAPSHOT_SUPPORT = 'revert_to_snapshot_support'
MOUNT_SNAPSHOT_SUPPORT = 'mount_snapshot_support' MOUNT_SNAPSHOT_SUPPORT = 'mount_snapshot_support'
CONSISTENT_SNAPSHOT_SUPPORT = 'consistent_snapshot_support'
BOOL_SPECS = ( BOOL_SPECS = (
SNAPSHOT_SUPPORT, SNAPSHOT_SUPPORT,
CREATE_SHARE_FROM_SNAPSHOT_SUPPORT, CREATE_SHARE_FROM_SNAPSHOT_SUPPORT,
REVERT_TO_SNAPSHOT_SUPPORT, REVERT_TO_SNAPSHOT_SUPPORT,
MOUNT_SNAPSHOT_SUPPORT MOUNT_SNAPSHOT_SUPPORT,
)
# share group types
GROUP_BOOL_SPECS = (
CONSISTENT_SNAPSHOT_SUPPORT,
) )
REPLICA_GRADUATION_VERSION = '2.56' REPLICA_GRADUATION_VERSION = '2.56'

View File

@ -12,11 +12,16 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import logging
from oslo_utils import strutils from oslo_utils import strutils
from manilaclient.common._i18n import _
from manilaclient.common import constants from manilaclient.common import constants
from manilaclient import exceptions from manilaclient import exceptions
LOG = logging.getLogger(__name__)
def extract_key_value_options(pairs): def extract_key_value_options(pairs):
result_dict = {} result_dict = {}
@ -62,29 +67,58 @@ def extract_properties(properties):
return result_dict return result_dict
def extract_extra_specs(extra_specs, specs_to_add): def extract_extra_specs(extra_specs, specs_to_add,
for item in specs_to_add: bool_specs=constants.BOOL_SPECS):
(key, value) = item.split('=', 1) try:
if key in extra_specs: for item in specs_to_add:
msg = ("Argument '%s' value specified twice." % key) (key, value) = item.split('=', 1)
raise exceptions.CommandError(msg) if key in extra_specs:
elif key in constants.BOOL_SPECS: msg = ("Argument '%s' value specified twice." % key)
if strutils.is_valid_boolstr(value):
extra_specs[key] = value.capitalize()
else:
msg = (
"Argument '%s' is of boolean "
"type and has invalid value: %s"
% (key, str(value)))
raise exceptions.CommandError(msg) raise exceptions.CommandError(msg)
else: elif key in bool_specs:
extra_specs[key] = value if strutils.is_valid_boolstr(value):
extra_specs[key] = value.capitalize()
else:
msg = (
"Argument '%s' is of boolean "
"type and has invalid value: %s"
% (key, str(value)))
raise exceptions.CommandError(msg)
else:
extra_specs[key] = value
except ValueError:
msg = LOG.error(_(
"Wrong format: specs should be key=value pairs."))
raise exceptions.CommandError(msg)
return extra_specs return extra_specs
def extract_group_specs(extra_specs, specs_to_add):
return extract_extra_specs(extra_specs,
specs_to_add, constants.GROUP_BOOL_SPECS)
def format_column_headers(columns): def format_column_headers(columns):
column_headers = [] column_headers = []
for column in columns: for column in columns:
column_headers.append( column_headers.append(
column.replace('_', ' ').title().replace('Id', 'ID')) column.replace('_', ' ').title().replace('Id', 'ID'))
return column_headers return column_headers
def format_share_group_type(share_group_type, formatter='table'):
printable_share_group_type = share_group_type._info
is_public = printable_share_group_type.pop('is_public')
printable_share_group_type['visibility'] = (
'public' if is_public else 'private')
if formatter == 'table':
printable_share_group_type['group_specs'] = (
format_properties(share_group_type.group_specs))
printable_share_group_type['share_types'] = (
"\n".join(printable_share_group_type['share_types'])
)
return printable_share_group_type

View File

@ -0,0 +1,163 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from openstackclient.identity import common as identity_common
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils as oscutils
from manilaclient.common._i18n import _
from manilaclient.common.apiclient import utils as apiutils
LOG = logging.getLogger(__name__)
class ShareGroupTypeAccessAllow(command.Command):
"""Allow a project to access a share group type."""
_description = _("Allow a project to access a share group type "
"(Admin only).")
def get_parser(self, prog_name):
parser = super(ShareGroupTypeAccessAllow, self).get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Share group type name or ID to allow access to.")
)
parser.add_argument(
'projects',
metavar="<project>",
nargs="+",
help=_("Project Name or ID to add share group type access for.")
)
identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
identity_client = self.app.client_manager.identity
result = 0
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
for project in parsed_args.projects:
try:
project_obj = identity_common.find_project(
identity_client, project, parsed_args.project_domain)
share_client.share_group_type_access.add_project_access(
share_group_type, project_obj.id)
except Exception as e:
result += 1
LOG.error(_(
"Failed to allow access for project '%(project)s' "
"to share group type with name or ID "
"'%(share_group_type)s': %(e)s"),
{'project': project,
'share_group_type': share_group_type, 'e': e})
if result > 0:
total = len(parsed_args.projects)
msg = (_("Failed to allow access to "
"%(result)s of %(total)s projects") % {'result': result,
'total': total})
raise exceptions.CommandError(msg)
class ListShareGroupTypeAccess(command.Lister):
"""Get access list for share group type."""
_description = _("Get access list for share group type (Admin only).")
def get_parser(self, prog_name):
parser = super(ListShareGroupTypeAccess, self).get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Filter results by share group type name or ID.")
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
if share_group_type._info.get('is_public'):
raise exceptions.CommandError(
'Forbidden to get access list for public share group type.')
data = share_client.share_group_type_access.list(share_group_type)
columns = ['Project ID']
values = (oscutils.get_item_properties(s, columns) for s in data)
return (columns, values)
class ShareGroupTypeAccessDeny(command.Command):
"""Deny a project to access a share group type."""
_description = _("Deny a project to access a share group type "
"(Admin only).")
def get_parser(self, prog_name):
parser = super(ShareGroupTypeAccessDeny, self).get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Share group type name or ID to deny access from")
)
parser.add_argument(
'projects',
metavar="<project>",
nargs="+",
help=_("Project Name(s) or ID(s) "
"to deny share group type access for.")
)
identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
identity_client = self.app.client_manager.identity
result = 0
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
for project in parsed_args.projects:
try:
project_obj = identity_common.find_project(
identity_client,
project,
parsed_args.project_domain)
share_client.share_group_type_access.remove_project_access(
share_group_type, project_obj.id)
except Exception as e:
result += 1
LOG.error(_(
"Failed to deny access for project '%(project)s' "
"to share group type with name or ID "
"'%(share_group_type)s': %(e)s"),
{'project': project,
'share_group_type': share_group_type, 'e': e})
if result > 0:
total = len(parsed_args.projects)
msg = (_("Failed to deny access to "
"%(result)s of %(total)s projects") % {'result': result,
'total': total})
raise exceptions.CommandError(msg)

View File

@ -0,0 +1,341 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import logging
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils as oscutils
from oslo_utils import strutils
from manilaclient.common._i18n import _
from manilaclient.common.apiclient import utils as apiutils
from manilaclient.osc import utils
LOG = logging.getLogger(__name__)
ATTRIBUTES = [
'id',
'name',
'share_types',
'visibility',
'is_default',
'group_specs'
]
class CreateShareGroupType(command.ShowOne):
"""Create new share group type."""
_description = _(
"Create new share group type")
log = logging.getLogger(__name__ + ".CreateShareGroupType")
def get_parser(self, prog_name):
parser = super(CreateShareGroupType, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar="<name>",
default=None,
help=_('Share group type name')
)
parser.add_argument(
"share_types",
metavar="<share-types>",
nargs="+",
default=None,
help=_("List of share type names or IDs. Example:"
" my-share-type-1 my-share-type-2"),
)
parser.add_argument(
"--group-specs",
type=str,
nargs='*',
metavar='<key=value>',
default=None,
help=_("Share Group type extra specs by key and value."
" OPTIONAL: Default=None. Example:"
" --group-specs consistent_snapshot_support=host."),
)
parser.add_argument(
'--public',
metavar="<public>",
default=True,
help=_('Make type accessible to the public (default true).')
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
kwargs = {
'name': parsed_args.name
}
share_types_list = []
for share_type in parsed_args.share_types:
try:
share_type_obj = apiutils.find_resource(
share_client.share_types,
share_type)
share_types_list.append(share_type_obj.name)
except Exception as e:
msg = LOG.error(_("Failed to find the share type with "
"name or ID '%(share_type)s': %(e)s"),
{'share_type': share_type, 'e': e})
raise exceptions.CommandError(msg)
kwargs['share_types'] = share_types_list
if parsed_args.public:
kwargs['is_public'] = strutils.bool_from_string(
parsed_args.public, default=True)
group_specs = {}
if parsed_args.group_specs:
for item in parsed_args.group_specs:
group_specs = utils.extract_group_specs(group_specs, [item])
kwargs['group_specs'] = group_specs
share_group_type = share_client.share_group_types.create(**kwargs)
formatter = parsed_args.formatter
formatted_group_type = utils.format_share_group_type(
share_group_type, formatter)
return (ATTRIBUTES, oscutils.get_dict_properties(
formatted_group_type, ATTRIBUTES))
class DeleteShareGroupType(command.Command):
"""Delete a share group type."""
_description = _("Delete a share group type")
log = logging.getLogger(__name__ + ".DeleteShareGroupType")
def get_parser(self, prog_name):
parser = super(DeleteShareGroupType, self).get_parser(prog_name)
parser.add_argument(
'share_group_types',
metavar="<share-group-types>",
nargs="+",
help=_("Name or ID of the share group type(s) to delete")
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
result = 0
for share_group_type in parsed_args.share_group_types:
try:
share_group_type_obj = apiutils.find_resource(
share_client.share_group_types,
share_group_type)
share_client.share_group_types.delete(share_group_type_obj)
except Exception as e:
result += 1
LOG.error(_(
"Failed to delete share group type with "
"name or ID '%(share_group_type)s': %(e)s"),
{'share_group_type': share_group_type, 'e': e})
if result > 0:
total = len(parsed_args.share_group_types)
msg = (_("%(result)s of %(total)s share group types failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListShareGroupType(command.Lister):
"""List Share Group Types."""
_description = _("List share types")
log = logging.getLogger(__name__ + ".ListShareGroupType")
def get_parser(self, prog_name):
parser = super(ListShareGroupType, self).get_parser(prog_name)
parser.add_argument(
'--all',
action='store_true',
default=False,
help=_('Display all share group types whether public or private. '
'Default=False. (Admin only)'),
)
parser.add_argument(
'--group-specs',
type=str,
nargs='*',
metavar='<key=value>',
default=None,
help=_('Filter share group types with group specs (key=value).'),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
search_opts = {}
if parsed_args.group_specs:
search_opts = {
'group_specs': utils.extract_group_specs(
extra_specs={},
specs_to_add=parsed_args.group_specs)
}
formatter = parsed_args.formatter
share_group_types = share_client.share_group_types.list(
search_opts=search_opts,
show_all=parsed_args.all)
formatted_types = []
for share_group_type in share_group_types:
formatted_types.append(utils.format_share_group_type(
share_group_type, formatter))
values = (oscutils.get_dict_properties(
sgt, ATTRIBUTES) for sgt in formatted_types)
return (ATTRIBUTES, values)
class ShowShareGroupType(command.ShowOne):
"""Show Share Group Types."""
_description = _("Show share group types")
log = logging.getLogger(__name__ + ".ShowShareGroupType")
def get_parser(self, prog_name):
parser = super(ShowShareGroupType, self).get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Name or ID of the share group type to show")
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
share_group_type = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
share_group_type_obj = share_client.share_group_types.get(
share_group_type)
formatter = parsed_args.formatter
formatted_group_type = utils.format_share_group_type(
share_group_type_obj, formatter)
return (ATTRIBUTES, oscutils.get_dict_properties(
formatted_group_type, ATTRIBUTES))
class SetShareGroupType(command.Command):
"""Set share type properties."""
_description = _("Set share group type properties")
log = logging.getLogger(__name__ + ".SetShareGroupType")
def get_parser(self, prog_name):
parser = super(SetShareGroupType, self).get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Name or ID of the share group type to modify")
)
parser.add_argument(
"--group-specs",
type=str,
nargs='*',
metavar='<key=value>',
default=None,
help=_("Extra specs key and value of share group type that will be"
" used for share type creation. OPTIONAL: Default=None."
" Example: --group-specs consistent-snapshot-support=True"),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
try:
share_group_type_obj = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
except Exception as e:
msg = LOG.error(_(
"Failed to find the share group type with "
"name or ID '%(share_group_type)s': %(e)s"),
{'share_group_type': parsed_args.share_group_type, 'e': e})
raise exceptions.CommandError(msg)
kwargs = {}
if kwargs:
share_group_type_obj.set_keys(**kwargs)
if parsed_args.group_specs:
group_specs = utils.extract_group_specs(
extra_specs={},
specs_to_add=parsed_args.group_specs)
try:
share_group_type_obj.set_keys(group_specs)
except Exception as e:
raise exceptions.CommandError(
"Failed to set share group type key: %s" % e)
class UnsetShareGroupType(command.Command):
"""Unset share group type extra specs."""
_description = _("Unset share group type extra specs")
log = logging.getLogger(__name__ + ".UnsetShareGroupType")
def get_parser(self, prog_name):
parser = super(UnsetShareGroupType, self).get_parser(prog_name)
parser.add_argument(
'share_group_type',
metavar="<share-group-type>",
help=_("Name or ID of the share grouptype to modify")
)
parser.add_argument(
'group_specs',
metavar='<key>',
nargs='+',
help=_('Remove group specs from this share group type'),
)
return parser
def take_action(self, parsed_args):
share_client = self.app.client_manager.share
try:
share_group_type_obj = apiutils.find_resource(
share_client.share_group_types, parsed_args.share_group_type)
except Exception as e:
msg = LOG.error(_(
"Failed to find the share group type with "
"name or ID '%(share_group_type)s': %(e)s"),
{'share_group_type': parsed_args.share_group_type, 'e': e})
raise exceptions.CommandError(msg)
if parsed_args.group_specs:
try:
share_group_type_obj.unset_keys(parsed_args.group_specs)
except Exception as e:
raise exceptions.CommandError(
"Failed to remove share type group extra spec: %s" % e)

View File

@ -55,6 +55,8 @@ class FakeShareClient(object):
self.share_instances = mock.Mock() self.share_instances = mock.Mock()
self.pools = mock.Mock() self.pools = mock.Mock()
self.limits = mock.Mock() self.limits = mock.Mock()
self.share_group_types = mock.Mock()
self.share_group_type_access = mock.Mock()
class ManilaParseException(Exception): class ManilaParseException(Exception):
@ -826,7 +828,7 @@ class FakeShareAvailabilityZones(object):
:param Dictionary attrs: :param Dictionary attrs:
A dictionary with all attributes A dictionary with all attributes
:param Integer count: :param Integer count:
The number of share types to be faked The number of availability zones to be faked
:return: :return:
A list of FakeResource objects A list of FakeResource objects
""" """
@ -1084,6 +1086,7 @@ class FakeShareNetwork(object):
A dictionary with all attributes A dictionary with all attributes
:param Integer count: :param Integer count:
The number of share networks to be faked The number of share networks to be faked
:return: :return:
A list of FakeResource objects A list of FakeResource objects
""" """
@ -1211,3 +1214,78 @@ class FakeShareGroup(object):
share_groups.append( share_groups.append(
FakeShareGroup.create_one_share_group(attrs)) FakeShareGroup.create_one_share_group(attrs))
return share_groups return share_groups
class FakeShareGroupType(object):
"""Fake one or more share group types"""
@staticmethod
def create_one_share_group_type(attrs=None, methods=None):
"""Create a fake share group type
:param Dictionary attrs:
A dictionary with all attributes
:return:
A FakeResource object, with project_id, resource and so on
"""
attrs = attrs or {}
methods = methods or {}
share_group_type_info = {
"is_public": True,
"group_specs": {
"snapshot_support": True
},
"share_types": ['share-types-id-' + uuid.uuid4().hex],
"id": 'share-group-type-id-' + uuid.uuid4().hex,
"name": 'share-group-type-name-' + uuid.uuid4().hex,
"is_default": False
}
share_group_type_info.update(attrs)
share_group_type = osc_fakes.FakeResource(info=copy.deepcopy(
share_group_type_info),
methods=methods,
loaded=True)
return share_group_type
@staticmethod
def create_share_group_types(attrs=None, count=2):
"""Create multiple fake share group types.
:param Dictionary attrs:
A dictionary with all attributes
:param Integer count:
The number of share group types to be faked
:return:
A list of FakeResource objects
"""
share_group_types = []
for n in range(0, count):
share_group_types.append(
FakeShareGroupType.create_one_share_group_type(attrs))
return share_group_types
@staticmethod
def get_share_group_types(share_group_types=None, count=2):
"""Get an iterable MagicMock object with a list of faked group types.
If types list is provided, then initialize the Mock object with the
list. Otherwise create one.
:param List types:
A list of FakeResource objects faking types
:param Integer count:
The number of group types to be faked
:return
An iterable Mock object with side_effect set to a list of faked
group types
"""
if share_group_types is None:
share_group_types = FakeShareGroupType.share_group_types(count)
return mock.Mock(side_effect=share_group_types)

View File

@ -0,0 +1,520 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from unittest import mock
from osc_lib import exceptions
from osc_lib import utils as oscutils
from manilaclient import api_versions
from manilaclient.common.apiclient.exceptions import BadRequest
from manilaclient.common.apiclient.exceptions import NotFound
from manilaclient.osc import utils
from manilaclient.osc.v2 import share_group_types as osc_share_group_types
from manilaclient.tests.unit.osc import osc_utils
from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes
COLUMNS = [
'id',
'name',
'share_types',
'visibility',
'is_default',
'group_specs',
]
class TestShareGroupType(manila_fakes.TestShare):
def setUp(self):
super(TestShareGroupType, self).setUp()
self.sgt_mock = self.app.client_manager.share.share_group_types
self.sgt_mock.reset_mock()
self.app.client_manager.share.api_version = api_versions.APIVersion(
api_versions.MAX_VERSION)
class TestShareGroupTypeCreate(TestShareGroupType):
def setUp(self):
super(TestShareGroupTypeCreate, self).setUp()
self.share_types = (
manila_fakes.FakeShareType.create_share_types(count=2))
formatted_share_types = []
for st in self.share_types:
formatted_share_types.append(st.name)
self.share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={
'share_types': formatted_share_types
}
))
self.share_group_type_formatted = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={
'id': self.share_group_type['id'],
'name': self.share_group_type['name'],
'share_types': formatted_share_types
}
))
formatted_sgt = utils.format_share_group_type(
self.share_group_type_formatted)
self.sgt_mock.create.return_value = self.share_group_type
self.sgt_mock.get.return_value = self.share_group_type
# Get the command object to test
self.cmd = osc_share_group_types.CreateShareGroupType(self.app, None)
self.data = tuple(formatted_sgt.values())
self.columns = tuple(formatted_sgt.keys())
def test_share_group_type_create_required_args(self):
"""Verifies required arguments."""
arglist = [
self.share_group_type.name,
self.share_types[0].name,
self.share_types[1].name,
]
verifylist = [
('name', self.share_group_type.name),
('share_types', [self.share_types[0].name,
self.share_types[1].name])
]
with mock.patch(
'manilaclient.common.apiclient.utils.find_resource',
side_effect=[self.share_types[0], self.share_types[1]]):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.create.assert_called_with(
group_specs={},
is_public=True,
name=self.share_group_type.name,
share_types=[
self.share_types[0].name, self.share_types[1].name]
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_share_group_type_create_missing_required_arg(self):
"""Verifies missing required arguments."""
arglist = [
self.share_group_type.name,
]
verifylist = [
('name', self.share_group_type.name)
]
self.assertRaises(osc_utils.ParserException,
self.check_parser, self.cmd, arglist, verifylist)
def test_share_group_type_create_private(self):
arglist = [
self.share_group_type.name,
self.share_types[0].name,
self.share_types[1].name,
'--public', 'False'
]
verifylist = [
('name', self.share_group_type.name),
('share_types', [self.share_types[0].name,
self.share_types[1].name]),
('public', 'False')
]
with mock.patch(
'manilaclient.common.apiclient.utils.find_resource',
side_effect=[self.share_types[0],
self.share_types[1]]):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.create.assert_called_with(
group_specs={},
is_public=False,
name=self.share_group_type.name,
share_types=[self.share_types[0].name,
self.share_types[1].name]
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_share_group_type_create_group_specs(self):
arglist = [
self.share_group_type.name,
self.share_types[0].name,
self.share_types[1].name,
'--group-specs', 'consistent_snapshot_support=true'
]
verifylist = [
('name', self.share_group_type.name),
('share_types', [self.share_types[0].name,
self.share_types[1].name]),
('group_specs', ['consistent_snapshot_support=true'])
]
with mock.patch(
'manilaclient.common.apiclient.utils.find_resource',
side_effect=[self.share_types[0],
self.share_types[1]]):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.create.assert_called_with(
group_specs={'consistent_snapshot_support': 'True'},
is_public=True,
name=self.share_group_type.name,
share_types=[
self.share_types[0].name, self.share_types[1].name]
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_create_share_group_type(self):
arglist = [
self.share_group_type.name,
self.share_types[0].name,
self.share_types[1].name
]
verifylist = [
('name', self.share_group_type.name),
('share_types', [self.share_types[0].name,
self.share_types[1].name])
]
with mock.patch(
'manilaclient.common.apiclient.utils.find_resource',
side_effect=[self.share_types[0],
self.share_types[1],
self.share_group_type]):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.create.assert_called_with(
group_specs={},
is_public=True,
name=self.share_group_type.name,
share_types=[self.share_types[0].name,
self.share_types[1].name]
)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
class TestShareGroupTypeDelete(TestShareGroupType):
def setUp(self):
super(TestShareGroupTypeDelete, self).setUp()
self.share_group_types = (
manila_fakes.FakeShareGroupType.create_share_group_types(count=2))
self.sgt_mock.delete.return_value = None
self.sgt_mock.get = (
manila_fakes.FakeShareGroupType.get_share_group_types(
self.share_group_types))
# Get the command object to test
self.cmd = osc_share_group_types.DeleteShareGroupType(self.app, None)
def test_share_group_type_delete_one(self):
arglist = [
self.share_group_types[0].name
]
verifylist = [
('share_group_types', [self.share_group_types[0].name])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.sgt_mock.delete.assert_called_with(
self.share_group_types[0])
self.assertIsNone(result)
def test_share_group_type_delete_multiple(self):
arglist = []
for t in self.share_group_types:
arglist.append(t.name)
verifylist = [
('share_group_types', arglist),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
calls = []
for t in self.share_group_types:
calls.append(mock.call(t))
self.sgt_mock.delete.assert_has_calls(calls)
self.assertIsNone(result)
def test_delete_share_group_type_with_exception(self):
arglist = [
'non_existing_type',
]
verifylist = [
('share_group_types', arglist),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.sgt_mock.delete.side_effect = exceptions.CommandError()
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
def test_delete_share_group_type(self):
arglist = [
self.share_group_types[0].name
]
verifylist = [
('share_group_types', [self.share_group_types[0].name])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.sgt_mock.delete.assert_called_with(self.share_group_types[0])
self.assertIsNone(result)
class TestShareGroupTypeSet(TestShareGroupType):
def setUp(self):
super(TestShareGroupTypeSet, self).setUp()
self.share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
methods={'set_keys': None, 'update': None}))
self.sgt_mock.get.return_value = self.share_group_type
# Get the command object to test
self.cmd = osc_share_group_types.SetShareGroupType(self.app, None)
def test_share_group_type_set_group_specs(self):
arglist = [
self.share_group_type.id,
'--group-specs', 'consistent_snapshot_support=true'
]
verifylist = [
('share_group_type', self.share_group_type.id),
('group_specs', ['consistent_snapshot_support=true'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.share_group_type.set_keys.assert_called_with(
{'consistent_snapshot_support': 'True'})
self.assertIsNone(result)
def test_share_group_type_set_extra_specs_exception(self):
arglist = [
self.share_group_type.id,
'--group-specs', 'snapshot_support=true'
]
verifylist = [
('share_group_type', self.share_group_type.id),
('group_specs', ['snapshot_support=true'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.share_group_type.set_keys.side_effect = BadRequest()
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
class TestShareGroupTypeUnset(TestShareGroupType):
def setUp(self):
super(TestShareGroupTypeUnset, self).setUp()
self.share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
methods={'unset_keys': None}))
self.sgt_mock.get.return_value = self.share_group_type
# Get the command object to test
self.cmd = osc_share_group_types.UnsetShareGroupType(self.app, None)
def test_share_group_type_unset_extra_specs(self):
arglist = [
self.share_group_type.id,
'consistent_snapshot_support'
]
verifylist = [
('share_group_type', self.share_group_type.id),
('group_specs', ['consistent_snapshot_support'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.share_group_type.unset_keys.assert_called_with(
['consistent_snapshot_support'])
self.assertIsNone(result)
def test_share_group_type_unset_exception(self):
arglist = [
self.share_group_type.id,
'snapshot_support'
]
verifylist = [
('share_group_type', self.share_group_type.id),
('group_specs', ['snapshot_support'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.share_group_type.unset_keys.side_effect = NotFound()
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
class TestShareGroupTypeList(TestShareGroupType):
def setUp(self):
super(TestShareGroupTypeList, self).setUp()
self.share_group_types = (
manila_fakes.FakeShareGroupType.create_share_group_types())
self.sgt_mock.list.return_value = self.share_group_types
# Get the command object to test
self.cmd = osc_share_group_types.ListShareGroupType(self.app, None)
self.values = (oscutils.get_dict_properties(
s._info, COLUMNS) for s in self.share_group_types)
def test_share_group_type_list_no_options(self):
arglist = []
verifylist = [
('all', False)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.list.assert_called_once_with(
search_opts={},
show_all=False
)
self.assertEqual(COLUMNS, columns)
self.assertEqual(list(self.values), list(data))
def test_share_group_type_list_all(self):
arglist = [
'--all',
]
verifylist = [
('all', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.list.assert_called_once_with(
search_opts={},
show_all=True)
self.assertEqual(COLUMNS, columns)
self.assertEqual(list(self.values), list(data))
def test_share_group_type_list_group_specs(self):
arglist = [
'--group-specs', 'consistent_snapshot_support=true'
]
verifylist = [
('group_specs', ['consistent_snapshot_support=true'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.list.assert_called_once_with(
search_opts={'group_specs': {
'consistent_snapshot_support': 'True'}},
show_all=False)
self.assertEqual(COLUMNS, columns)
self.assertEqual(list(self.values), list(data))
class TestShareGroupTypeShow(TestShareGroupType):
def setUp(self):
super(TestShareGroupTypeShow, self).setUp()
self.share_types = (
manila_fakes.FakeShareType.create_share_types(count=2))
formatted_share_types = []
for st in self.share_types:
formatted_share_types.append(st.name)
self.share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={
'share_types': formatted_share_types
}
))
self.share_group_type_formatted = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={
'id': self.share_group_type['id'],
'name': self.share_group_type['name'],
'share_types': formatted_share_types
}
))
formatted_sgt = utils.format_share_group_type(
self.share_group_type_formatted)
self.sgt_mock.get.return_value = self.share_group_type
# Get the command object to test
self.cmd = osc_share_group_types.ShowShareGroupType(self.app, None)
self.data = tuple(formatted_sgt.values())
self.columns = tuple(formatted_sgt.keys())
def test_share_group_type_show(self):
arglist = [
self.share_group_type.name
]
verifylist = [
("share_group_type", self.share_group_type.name)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.sgt_mock.get.assert_called_with(self.share_group_type)
self.assertCountEqual(self.columns, columns)
self.assertCountEqual(self.data, data)

View File

@ -0,0 +1,204 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
from osc_lib import exceptions
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
from manilaclient.common.apiclient.exceptions import BadRequest
from manilaclient.osc.v2 import share_group_type_access as osc_sgta
from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes
class TestShareGroupTypeAccess(manila_fakes.TestShare):
def setUp(self):
super(TestShareGroupTypeAccess, self).setUp()
self.type_access_mock = (
self.app.client_manager.share.share_group_type_access)
self.type_access_mock.reset_mock()
self.share_group_types_mock = (
self.app.client_manager.share.share_group_types)
self.share_group_types_mock.reset_mock()
self.projects_mock = self.app.client_manager.identity.projects
self.projects_mock.reset_mock()
class TestShareGroupTypeAccessAllow(TestShareGroupTypeAccess):
def setUp(self):
super(TestShareGroupTypeAccessAllow, self).setUp()
self.project = identity_fakes.FakeProject.create_one_project()
self.share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={'is_public': False}
)
)
self.share_group_types_mock.get.return_value = self.share_group_type
self.projects_mock.get.return_value = self.project
self.type_access_mock.add_project_access.return_value = None
# Get the command object to test
self.cmd = osc_sgta.ShareGroupTypeAccessAllow(self.app, None)
def test_share_group_type_access_create(self):
arglist = [
self.share_group_type.id,
self.project.id
]
verifylist = [
('share_group_type', self.share_group_type.id),
('projects', [self.project.id])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.type_access_mock.add_project_access.assert_called_with(
self.share_group_type, self.project.id)
self.assertIsNone(result)
def test_share_group_type_access_create_invalid_project_exception(self):
arglist = [
self.share_group_type.id,
'invalid_project_format'
]
verifylist = [
('share_group_type', self.share_group_type.id),
('projects', ['invalid_project_format'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.type_access_mock.add_project_access.side_effect = BadRequest()
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
class TestShareGroupTypeAccessList(TestShareGroupTypeAccess):
columns = ['Project ID']
data = (('',), ('',))
def setUp(self):
super(TestShareGroupTypeAccessList, self).setUp()
self.type_access_mock.list.return_value = (
self.columns, self.data)
# Get the command object to test
self.cmd = osc_sgta.ListShareGroupTypeAccess(self.app, None)
def test_share_group_type_access_list(self):
share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={'is_public': False}
)
)
self.share_group_types_mock.get.return_value = share_group_type
arglist = [
share_group_type.id,
]
verifylist = [
('share_group_type', share_group_type.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.type_access_mock.list.assert_called_once_with(
share_group_type)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, tuple(data))
def test_share_group_type_access_list_public_type(self):
share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={'is_public': True}
)
)
self.share_group_types_mock.get.return_value = share_group_type
arglist = [
share_group_type.id,
]
verifylist = [
('share_group_type', share_group_type.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)
class TestShareGroupTypeAccessDeny(TestShareGroupTypeAccess):
def setUp(self):
super(TestShareGroupTypeAccessDeny, self).setUp()
self.project = identity_fakes.FakeProject.create_one_project()
self.share_group_type = (
manila_fakes.FakeShareGroupType.create_one_share_group_type(
attrs={'is_public': False}))
self.share_group_types_mock.get.return_value = self.share_group_type
self.projects_mock.get.return_value = self.project
self.type_access_mock.remove_project_access.return_value = None
# Get the command object to test
self.cmd = osc_sgta.ShareGroupTypeAccessDeny(self.app, None)
def test_share_group_type_access_delete(self):
arglist = [
self.share_group_type.id,
self.project.id
]
verifylist = [
('share_group_type', self.share_group_type.id),
('projects', [self.project.id])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.type_access_mock.remove_project_access.assert_called_with(
self.share_group_type, self.project.id)
self.assertIsNone(result)
def test_share_group_type_access_delete_exception(self):
arglist = [
self.share_group_type.id,
'invalid_project_format'
]
verifylist = [
('share_group_type', self.share_group_type.id),
('projects', ['invalid_project_format'])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.type_access_mock.remove_project_access.side_effect = BadRequest()
self.assertRaises(
exceptions.CommandError, self.cmd.take_action, parsed_args)

View File

@ -152,7 +152,10 @@ class ShareGroupTypeManager(base.ManagerWithFind):
:rtype: list of :class:`ShareGroupType`. :rtype: list of :class:`ShareGroupType`.
""" """
query_string = '?is_public=all' if show_all else '' search_opts = search_opts or {}
if show_all:
search_opts['is_public'] = 'all'
query_string = self._build_query_string(search_opts)
url = RESOURCES_PATH + query_string url = RESOURCES_PATH + query_string
return self._list(url, RESOURCES_NAME) return self._list(url, RESOURCES_NAME)

View File

@ -122,6 +122,15 @@ openstack.share.v2 =
share_group_show = manilaclient.osc.v2.share_groups:ShowShareGroup share_group_show = manilaclient.osc.v2.share_groups:ShowShareGroup
share_group_set = manilaclient.osc.v2.share_groups:SetShareGroup share_group_set = manilaclient.osc.v2.share_groups:SetShareGroup
share_group_unset = manilaclient.osc.v2.share_groups:UnsetShareGroup share_group_unset = manilaclient.osc.v2.share_groups:UnsetShareGroup
share_group_type_create = manilaclient.osc.v2.share_group_types:CreateShareGroupType
share_group_type_delete = manilaclient.osc.v2.share_group_types:DeleteShareGroupType
share_group_type_list = manilaclient.osc.v2.share_group_types:ListShareGroupType
share_group_type_show = manilaclient.osc.v2.share_group_types:ShowShareGroupType
share_group_type_set = manilaclient.osc.v2.share_group_types:SetShareGroupType
share_group_type_unset = manilaclient.osc.v2.share_group_types:UnsetShareGroupType
share_group_type_access_create = manilaclient.osc.v2.share_group_type_access:ShareGroupTypeAccessAllow
share_group_type_access_list = manilaclient.osc.v2.share_group_type_access:ListShareGroupTypeAccess
share_group_type_access_delete = manilaclient.osc.v2.share_group_type_access:ShareGroupTypeAccessDeny
[coverage:run] [coverage:run]
omit = manilaclient/tests/* omit = manilaclient/tests/*