diff --git a/openstackclient/tests/unit/volume/v2/test_volume_type.py b/openstackclient/tests/unit/volume/v2/test_volume_type.py index 084e23745..0f6e65a22 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_type.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_type.py @@ -15,6 +15,7 @@ from unittest import mock from unittest.mock import call +from cinderclient import api_versions from osc_lib.cli import format_columns from osc_lib import exceptions from osc_lib import utils @@ -327,7 +328,9 @@ class TestTypeList(TestType): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with(is_public=None) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=None + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -344,7 +347,9 @@ class TestTypeList(TestType): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with(is_public=True) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=True + ) self.assertEqual(self.columns_long, columns) self.assertCountEqual(self.data_long, list(data)) @@ -360,7 +365,9 @@ class TestTypeList(TestType): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.volume_types_mock.list.assert_called_once_with(is_public=False) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=False + ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, list(data)) @@ -381,6 +388,60 @@ class TestTypeList(TestType): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data_with_default_type, list(data)) + def test_type_list_with_property_option(self): + self.app.client_manager.volume.api_version = api_versions.APIVersion( + '3.52' + ) + + arglist = [ + "--property", + "multiattach= True", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", False), + ("properties", {"multiattach": " True"}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.volume_types_mock.list.assert_called_once_with( + search_opts={"extra_specs": {"multiattach": " True"}}, + is_public=None, + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, list(data)) + + def test_type_list_with_property_option_pre_v352(self): + self.app.client_manager.volume.api_version = api_versions.APIVersion( + '3.51' + ) + + arglist = [ + "--property", + "multiattach= True", + ] + verifylist = [ + ("encryption_type", False), + ("long", False), + ("is_public", None), + ("default", False), + ("properties", {"multiattach": " True"}), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + exc = self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + self.assertIn( + '--os-volume-api-version 3.52 or greater is required', + str(exc), + ) + def test_type_list_with_encryption(self): encryption_type = volume_fakes.create_one_encryption_volume_type( attrs={'volume_type_id': self.volume_types[0].id}, @@ -426,7 +487,9 @@ class TestTypeList(TestType): columns, data = self.cmd.take_action(parsed_args) self.volume_encryption_types_mock.list.assert_called_once_with() - self.volume_types_mock.list.assert_called_once_with(is_public=None) + self.volume_types_mock.list.assert_called_once_with( + search_opts={}, is_public=None + ) self.assertEqual(encryption_columns, columns) self.assertCountEqual(encryption_data, list(data)) @@ -459,7 +522,7 @@ class TestTypeSet(TestType): verifylist = [ ('name', 'new_name'), ('description', 'new_description'), - ('property', None), + ('properties', None), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -489,7 +552,7 @@ class TestTypeSet(TestType): verifylist = [ ('name', None), ('description', None), - ('property', {'myprop': 'myvalue'}), + ('properties', {'myprop': 'myvalue'}), ('volume_type', self.volume_type.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -857,7 +920,7 @@ class TestTypeUnset(TestType): self.volume_type.id, ] verifylist = [ - ('property', ['property', 'multi_property']), + ('properties', ['property', 'multi_property']), ('volume_type', self.volume_type.id), ] diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 3d8a50105..bf10a2e54 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -17,6 +17,7 @@ import functools import logging +from cinderclient import api_versions from cliff import columns as cliff_columns from osc_lib.cli import format_columns from osc_lib.cli import parseractions @@ -139,6 +140,7 @@ class CreateVolumeType(command.ShowOne): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Set a property on this volume type ' '(repeat option to set multiple properties)' @@ -232,11 +234,13 @@ class CreateVolumeType(command.ShowOne): "type: %(e)s" ) LOG.error(msg % {'project': parsed_args.project, 'e': e}) - if parsed_args.property: - result = volume_type.set_keys(parsed_args.property) + + if parsed_args.properties: + result = volume_type.set_keys(parsed_args.properties) volume_type._info.update( {'properties': format_columns.DictColumn(result)} ) + if ( parsed_args.encryption_provider or parsed_args.encryption_cipher @@ -261,6 +265,7 @@ class CreateVolumeType(command.ShowOne): volume_type._info.update( {'encryption': format_columns.DictColumn(encryption._info)} ) + volume_type._info.pop("os-volume-type-access:is_public", None) return zip(*sorted(volume_type._info.items())) @@ -348,6 +353,18 @@ class ListVolumeType(command.Lister): "(admin only)" ), ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + dest='properties', + help=_( + 'Filter by a property on the volume types ' + '(repeat option to filter by multiple properties) ' + '(admin only except for user-visible extra specs) ' + '(supported by --os-volume-api-version 3.52 or above)' + ), + ) return parser def take_action(self, parsed_args): @@ -375,8 +392,22 @@ class ListVolumeType(command.Lister): if parsed_args.default: data = [volume_client.volume_types.default()] else: + search_opts = {} + + if parsed_args.properties: + if volume_client.api_version < api_versions.APIVersion('3.52'): + msg = _( + "--os-volume-api-version 3.52 or greater is required " + "to use the '--property' option" + ) + raise exceptions.CommandError(msg) + + # we pass this through as-is + search_opts['extra_specs'] = parsed_args.properties + data = volume_client.volume_types.list( - is_public=parsed_args.is_public + search_opts=search_opts, + is_public=parsed_args.is_public, ) formatters = {'Extra Specs': format_columns.DictColumn} @@ -445,6 +476,7 @@ class SetVolumeType(command.Command): '--property', metavar='', action=parseractions.KeyValueAction, + dest='properties', help=_( 'Set a property on this volume type ' '(repeat option to set multiple properties)' @@ -555,9 +587,9 @@ class SetVolumeType(command.Command): ) result += 1 - if parsed_args.property: + if parsed_args.properties: try: - volume_type.set_keys(parsed_args.property) + volume_type.set_keys(parsed_args.properties) except Exception as e: LOG.error(_("Failed to set volume type property: %s"), e) result += 1 @@ -689,6 +721,7 @@ class UnsetVolumeType(command.Command): '--property', metavar='', action='append', + dest='properties', help=_( 'Remove a property from this volume type ' '(repeat option to remove multiple properties)' @@ -723,9 +756,9 @@ class UnsetVolumeType(command.Command): ) result = 0 - if parsed_args.property: + if parsed_args.properties: try: - volume_type.unset_keys(parsed_args.property) + volume_type.unset_keys(parsed_args.properties) except Exception as e: LOG.error(_("Failed to unset volume type property: %s"), e) result += 1 diff --git a/releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml b/releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml new file mode 100644 index 000000000..178622c47 --- /dev/null +++ b/releasenotes/notes/volume-type-list-properties-filter-8532f96d16733915.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + The ``volume type list`` command now accepts a ``--property =`` + option, allowing users to filter volume types by their extra spec + properties.