diff --git a/doc/source/cli/command-objects/block-storage-resource-filter.rst b/doc/source/cli/command-objects/block-storage-resource-filter.rst new file mode 100644 index 000000000..3e4c33ba5 --- /dev/null +++ b/doc/source/cli/command-objects/block-storage-resource-filter.rst @@ -0,0 +1,8 @@ +============================= +block storage resource filter +============================= + +Block Storage v3 + +.. autoprogram-cliff:: openstack.volume.v3 + :command: block storage resource filter * diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst index d9bedb36d..d789eceb5 100644 --- a/doc/source/cli/commands.rst +++ b/doc/source/cli/commands.rst @@ -77,6 +77,7 @@ referring to both Compute and Volume quotas. * ``aggregate``: (**Compute**) a grouping of compute hosts * ``availability zone``: (**Compute**, **Network**, **Volume**) a logical partition of hosts or block storage or network services * ``block storage cluster``: (**Volume**) clusters of volume services +* ``block storage resource filter``: (**Volume**) filters for volume service resources * ``catalog``: (**Identity**) service catalog * ``command``: (**Internal**) installed commands in the OSC process * ``compute agent``: (**Compute**) a cloud Compute agent available to a hypervisor diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv index 54ce79676..eeaaeacce 100644 --- a/doc/source/cli/data/cinder.csv +++ b/doc/source/cli/data/cinder.csv @@ -69,7 +69,7 @@ group-update,volume group set,Updates a group. (Supported by API versions 3.13 - image-metadata,volume set --image-property,Sets or deletes volume image metadata. image-metadata-show,volume show,Shows volume image metadata. list,volume list,Lists all volumes. -list-filters,,List enabled filters. (Supported by API versions 3.33 - 3.latest) +list-filters,block storage resource filter list,List enabled filters. (Supported by API versions 3.33 - 3.latest) manage,volume create --remote-source k=v,Manage an existing volume. manageable-list,,Lists all manageable volumes. (Supported by API versions 3.8 - 3.latest) message-delete,volume message delete,Removes one or more messages. (Supported by API versions 3.3 - 3.latest) diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py index 81ff0a988..e27d7fcac 100644 --- a/openstackclient/tests/unit/volume/v3/fakes.py +++ b/openstackclient/tests/unit/volume/v3/fakes.py @@ -42,6 +42,8 @@ class FakeVolumeClient(object): self.group_types.resource_class = fakes.FakeResource(None, {}) self.messages = mock.Mock() self.messages.resource_class = fakes.FakeResource(None, {}) + self.resource_filters = mock.Mock() + self.resource_filters.resource_class = fakes.FakeResource(None, {}) self.volumes = mock.Mock() self.volumes.resource_class = fakes.FakeResource(None, {}) self.volume_types = mock.Mock() @@ -124,6 +126,53 @@ class FakeCluster: return clusters +class FakeResourceFilter: + """Fake one or more resource filters.""" + + @staticmethod + def create_one_resource_filter(attrs=None): + """Create a fake resource filter. + + :param attrs: A dictionary with all attributes of resource filter + :return: A FakeResource object with id, name, status, etc. + """ + attrs = attrs or {} + + # Set default attribute + + resource_filter_info = { + 'filters': [ + 'name', + 'status', + 'image_metadata', + 'bootable', + 'migration_status', + ], + 'resource': 'volume', + } + + # Overwrite default attributes if there are some attributes set + resource_filter_info.update(attrs) + + return fakes.FakeResource(None, resource_filter_info, loaded=True) + + @staticmethod + def create_resource_filters(attrs=None, count=2): + """Create multiple fake resource filters. + + :param attrs: A dictionary with all attributes of resource filter + :param count: The number of resource filters to be faked + :return: A list of FakeResource objects + """ + resource_filters = [] + for n in range(0, count): + resource_filters.append( + FakeResourceFilter.create_one_resource_filter(attrs) + ) + + return resource_filters + + class FakeVolumeGroup: """Fake one or more volume groups.""" @@ -309,11 +358,10 @@ class FakeVolumeMessage: # Overwrite default attributes if there are some attributes set message_info.update(attrs) - message = fakes.FakeResource( + return fakes.FakeResource( None, message_info, loaded=True) - return message @staticmethod def create_volume_messages(attrs=None, count=2): @@ -402,11 +450,10 @@ class FakeVolumeAttachment: # Overwrite default attributes if there are some attributes set attachment_info.update(attrs) - attachment = fakes.FakeResource( + return fakes.FakeResource( None, attachment_info, loaded=True) - return attachment @staticmethod def create_volume_attachments(attrs=None, count=2): diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py new file mode 100644 index 000000000..b886726dd --- /dev/null +++ b/openstackclient/tests/unit/volume/v3/test_block_storage_resource_filter.py @@ -0,0 +1,144 @@ +# 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 cinderclient import api_versions +from osc_lib import exceptions + +from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes +from openstackclient.volume.v3 import block_storage_resource_filter + + +class TestBlockStorageResourceFilter(volume_fakes.TestVolume): + + def setUp(self): + super().setUp() + + # Get a shortcut to the ResourceFilterManager Mock + self.resource_filter_mock = \ + self.app.client_manager.volume.resource_filters + self.resource_filter_mock.reset_mock() + + +class TestBlockStorageResourceFilterList(TestBlockStorageResourceFilter): + + # The resource filters to be listed + fake_resource_filters = \ + volume_fakes.FakeResourceFilter.create_resource_filters() + + def setUp(self): + super().setUp() + + self.resource_filter_mock.list.return_value = \ + self.fake_resource_filters + + # Get the command object to test + self.cmd = block_storage_resource_filter\ + .ListBlockStorageResourceFilter(self.app, None) + + def test_resource_filter_list(self): + self.app.client_manager.volume.api_version = \ + api_versions.APIVersion('3.33') + + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + expected_columns = ('Resource', 'Filters') + expected_data = tuple( + ( + resource_filter.resource, + resource_filter.filters, + ) for resource_filter in self.fake_resource_filters + ) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, tuple(data)) + + # checking if proper call was made to list clusters + self.resource_filter_mock.list.assert_called_with() + + def test_resource_filter_list_pre_v333(self): + self.app.client_manager.volume.api_version = \ + api_versions.APIVersion('3.32') + + arglist = [] + verifylist = [] + 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.33 or greater is required', str(exc)) + + +class TestBlockStorageResourceFilterShow(TestBlockStorageResourceFilter): + + # The resource filters to be listed + fake_resource_filter = \ + volume_fakes.FakeResourceFilter.create_one_resource_filter() + + def setUp(self): + super().setUp() + + self.resource_filter_mock.list.return_value = \ + iter([self.fake_resource_filter]) + + # Get the command object to test + self.cmd = block_storage_resource_filter\ + .ShowBlockStorageResourceFilter(self.app, None) + + def test_resource_filter_show(self): + self.app.client_manager.volume.api_version = \ + api_versions.APIVersion('3.33') + + arglist = [ + self.fake_resource_filter.resource, + ] + verifylist = [ + ('resource', self.fake_resource_filter.resource), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + expected_columns = ('filters', 'resource') + expected_data = ( + self.fake_resource_filter.filters, + self.fake_resource_filter.resource, + ) + columns, data = self.cmd.take_action(parsed_args) + + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + + # checking if proper call was made to list clusters + self.resource_filter_mock.list.assert_called_with(resource='volume') + + def test_resource_filter_show_pre_v333(self): + self.app.client_manager.volume.api_version = \ + api_versions.APIVersion('3.32') + + arglist = [ + self.fake_resource_filter.resource, + ] + verifylist = [ + ('resource', self.fake_resource_filter.resource), + ] + 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.33 or greater is required', str(exc)) diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py new file mode 100644 index 000000000..4bcacf90c --- /dev/null +++ b/openstackclient/volume/v3/block_storage_resource_filter.py @@ -0,0 +1,83 @@ +# 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. + +"""Volume V3 Resource Filters implementations""" + +from cinderclient import api_versions +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils + +from openstackclient.i18n import _ + + +class ListBlockStorageResourceFilter(command.Lister): + _description = _('List block storage resource filters') + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + + if volume_client.api_version < api_versions.APIVersion('3.33'): + msg = _( + "--os-volume-api-version 3.33 or greater is required to " + "support the 'block storage resource filter list' command" + ) + raise exceptions.CommandError(msg) + + column_headers = ( + 'Resource', + 'Filters', + ) + + data = volume_client.resource_filters.list() + + return ( + column_headers, + (utils.get_item_properties(s, column_headers) for s in data) + ) + + +class ShowBlockStorageResourceFilter(command.ShowOne): + _description = _('Show filters for a block storage resource type') + + def get_parser(self, prog_name): + parser = super().get_parser(prog_name) + parser.add_argument( + 'resource', + metavar='', + help=_('Resource to show filters for (name).') + ) + + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + + if volume_client.api_version < api_versions.APIVersion('3.33'): + msg = _( + "--os-volume-api-version 3.33 or greater is required to " + "support the 'block storage resource filter show' command" + ) + raise exceptions.CommandError(msg) + + data = volume_client.resource_filters.list( + resource=parsed_args.resource + ) + if not data: + msg = _( + "No resource filter with a name of {parsed_args.resource}' " + "exists." + ) + raise exceptions.CommandError(msg) + resource_filter = next(data) + + return zip(*sorted(resource_filter._info.items())) diff --git a/setup.cfg b/setup.cfg index d5eaab187..0409121f4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -759,6 +759,8 @@ openstack.volume.v3 = block_storage_cluster_list = openstackclient.volume.v3.block_storage_cluster:ListBlockStorageCluster block_storage_cluster_set = openstackclient.volume.v3.block_storage_cluster:SetBlockStorageCluster block_storage_cluster_show = openstackclient.volume.v3.block_storage_cluster:ShowBlockStorageCluster + block_storage_resource_filter_list = openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter + block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot