Skip to content

Commit 53a7e67

Browse files
committed
volume: Add 'block storage resource filter list' command
These are based on the 'cinder list-filters' command, which accepts an optional '--resource {resource}' option to limit the listed filters to a single resource type. block storage resource filter list block storage resource filter show We used the 'block storage resource filter' terminology rather than simply 'resource filter' to highlight the fact that this is specific to the block storage service. Note that while this feature is a bit weird, good documentation can be found at [1]. [1] https://docs.openstack.org/cinder/latest/admin/generalized_filters.html Change-Id: I21e7c0ea427aef1f6665394d4b8e9a1f30d6dbb1 Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
1 parent d727a65 commit 53a7e67

7 files changed

Lines changed: 290 additions & 5 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
=============================
2+
block storage resource filter
3+
=============================
4+
5+
Block Storage v3
6+
7+
.. autoprogram-cliff:: openstack.volume.v3
8+
:command: block storage resource filter *

doc/source/cli/commands.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ referring to both Compute and Volume quotas.
7777
* ``aggregate``: (**Compute**) a grouping of compute hosts
7878
* ``availability zone``: (**Compute**, **Network**, **Volume**) a logical partition of hosts or block storage or network services
7979
* ``block storage cluster``: (**Volume**) clusters of volume services
80+
* ``block storage resource filter``: (**Volume**) filters for volume service resources
8081
* ``catalog``: (**Identity**) service catalog
8182
* ``command``: (**Internal**) installed commands in the OSC process
8283
* ``compute agent``: (**Compute**) a cloud Compute agent available to a hypervisor

doc/source/cli/data/cinder.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ group-update,volume group set,Updates a group. (Supported by API versions 3.13 -
6969
image-metadata,volume set --image-property,Sets or deletes volume image metadata.
7070
image-metadata-show,volume show,Shows volume image metadata.
7171
list,volume list,Lists all volumes.
72-
list-filters,,List enabled filters. (Supported by API versions 3.33 - 3.latest)
72+
list-filters,block storage resource filter list,List enabled filters. (Supported by API versions 3.33 - 3.latest)
7373
manage,volume create --remote-source k=v,Manage an existing volume.
7474
manageable-list,,Lists all manageable volumes. (Supported by API versions 3.8 - 3.latest)
7575
message-delete,volume message delete,Removes one or more messages. (Supported by API versions 3.3 - 3.latest)

openstackclient/tests/unit/volume/v3/fakes.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def __init__(self, **kwargs):
4242
self.group_types.resource_class = fakes.FakeResource(None, {})
4343
self.messages = mock.Mock()
4444
self.messages.resource_class = fakes.FakeResource(None, {})
45+
self.resource_filters = mock.Mock()
46+
self.resource_filters.resource_class = fakes.FakeResource(None, {})
4547
self.volumes = mock.Mock()
4648
self.volumes.resource_class = fakes.FakeResource(None, {})
4749
self.volume_types = mock.Mock()
@@ -124,6 +126,53 @@ def create_clusters(attrs=None, count=2):
124126
return clusters
125127

126128

129+
class FakeResourceFilter:
130+
"""Fake one or more resource filters."""
131+
132+
@staticmethod
133+
def create_one_resource_filter(attrs=None):
134+
"""Create a fake resource filter.
135+
136+
:param attrs: A dictionary with all attributes of resource filter
137+
:return: A FakeResource object with id, name, status, etc.
138+
"""
139+
attrs = attrs or {}
140+
141+
# Set default attribute
142+
143+
resource_filter_info = {
144+
'filters': [
145+
'name',
146+
'status',
147+
'image_metadata',
148+
'bootable',
149+
'migration_status',
150+
],
151+
'resource': 'volume',
152+
}
153+
154+
# Overwrite default attributes if there are some attributes set
155+
resource_filter_info.update(attrs)
156+
157+
return fakes.FakeResource(None, resource_filter_info, loaded=True)
158+
159+
@staticmethod
160+
def create_resource_filters(attrs=None, count=2):
161+
"""Create multiple fake resource filters.
162+
163+
:param attrs: A dictionary with all attributes of resource filter
164+
:param count: The number of resource filters to be faked
165+
:return: A list of FakeResource objects
166+
"""
167+
resource_filters = []
168+
for n in range(0, count):
169+
resource_filters.append(
170+
FakeResourceFilter.create_one_resource_filter(attrs)
171+
)
172+
173+
return resource_filters
174+
175+
127176
class FakeVolumeGroup:
128177
"""Fake one or more volume groups."""
129178

@@ -309,11 +358,10 @@ def create_one_volume_message(attrs=None):
309358
# Overwrite default attributes if there are some attributes set
310359
message_info.update(attrs)
311360

312-
message = fakes.FakeResource(
361+
return fakes.FakeResource(
313362
None,
314363
message_info,
315364
loaded=True)
316-
return message
317365

318366
@staticmethod
319367
def create_volume_messages(attrs=None, count=2):
@@ -402,11 +450,10 @@ def create_one_volume_attachment(attrs=None):
402450
# Overwrite default attributes if there are some attributes set
403451
attachment_info.update(attrs)
404452

405-
attachment = fakes.FakeResource(
453+
return fakes.FakeResource(
406454
None,
407455
attachment_info,
408456
loaded=True)
409-
return attachment
410457

411458
@staticmethod
412459
def create_volume_attachments(attrs=None, count=2):
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
13+
from cinderclient import api_versions
14+
from osc_lib import exceptions
15+
16+
from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
17+
from openstackclient.volume.v3 import block_storage_resource_filter
18+
19+
20+
class TestBlockStorageResourceFilter(volume_fakes.TestVolume):
21+
22+
def setUp(self):
23+
super().setUp()
24+
25+
# Get a shortcut to the ResourceFilterManager Mock
26+
self.resource_filter_mock = \
27+
self.app.client_manager.volume.resource_filters
28+
self.resource_filter_mock.reset_mock()
29+
30+
31+
class TestBlockStorageResourceFilterList(TestBlockStorageResourceFilter):
32+
33+
# The resource filters to be listed
34+
fake_resource_filters = \
35+
volume_fakes.FakeResourceFilter.create_resource_filters()
36+
37+
def setUp(self):
38+
super().setUp()
39+
40+
self.resource_filter_mock.list.return_value = \
41+
self.fake_resource_filters
42+
43+
# Get the command object to test
44+
self.cmd = block_storage_resource_filter\
45+
.ListBlockStorageResourceFilter(self.app, None)
46+
47+
def test_resource_filter_list(self):
48+
self.app.client_manager.volume.api_version = \
49+
api_versions.APIVersion('3.33')
50+
51+
arglist = []
52+
verifylist = []
53+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
54+
55+
expected_columns = ('Resource', 'Filters')
56+
expected_data = tuple(
57+
(
58+
resource_filter.resource,
59+
resource_filter.filters,
60+
) for resource_filter in self.fake_resource_filters
61+
)
62+
columns, data = self.cmd.take_action(parsed_args)
63+
64+
self.assertEqual(expected_columns, columns)
65+
self.assertEqual(expected_data, tuple(data))
66+
67+
# checking if proper call was made to list clusters
68+
self.resource_filter_mock.list.assert_called_with()
69+
70+
def test_resource_filter_list_pre_v333(self):
71+
self.app.client_manager.volume.api_version = \
72+
api_versions.APIVersion('3.32')
73+
74+
arglist = []
75+
verifylist = []
76+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
77+
78+
exc = self.assertRaises(
79+
exceptions.CommandError,
80+
self.cmd.take_action,
81+
parsed_args)
82+
self.assertIn(
83+
'--os-volume-api-version 3.33 or greater is required', str(exc))
84+
85+
86+
class TestBlockStorageResourceFilterShow(TestBlockStorageResourceFilter):
87+
88+
# The resource filters to be listed
89+
fake_resource_filter = \
90+
volume_fakes.FakeResourceFilter.create_one_resource_filter()
91+
92+
def setUp(self):
93+
super().setUp()
94+
95+
self.resource_filter_mock.list.return_value = \
96+
iter([self.fake_resource_filter])
97+
98+
# Get the command object to test
99+
self.cmd = block_storage_resource_filter\
100+
.ShowBlockStorageResourceFilter(self.app, None)
101+
102+
def test_resource_filter_show(self):
103+
self.app.client_manager.volume.api_version = \
104+
api_versions.APIVersion('3.33')
105+
106+
arglist = [
107+
self.fake_resource_filter.resource,
108+
]
109+
verifylist = [
110+
('resource', self.fake_resource_filter.resource),
111+
]
112+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
113+
114+
expected_columns = ('filters', 'resource')
115+
expected_data = (
116+
self.fake_resource_filter.filters,
117+
self.fake_resource_filter.resource,
118+
)
119+
columns, data = self.cmd.take_action(parsed_args)
120+
121+
self.assertEqual(expected_columns, columns)
122+
self.assertEqual(expected_data, data)
123+
124+
# checking if proper call was made to list clusters
125+
self.resource_filter_mock.list.assert_called_with(resource='volume')
126+
127+
def test_resource_filter_show_pre_v333(self):
128+
self.app.client_manager.volume.api_version = \
129+
api_versions.APIVersion('3.32')
130+
131+
arglist = [
132+
self.fake_resource_filter.resource,
133+
]
134+
verifylist = [
135+
('resource', self.fake_resource_filter.resource),
136+
]
137+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
138+
139+
exc = self.assertRaises(
140+
exceptions.CommandError,
141+
self.cmd.take_action,
142+
parsed_args)
143+
self.assertIn(
144+
'--os-volume-api-version 3.33 or greater is required', str(exc))
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
13+
"""Volume V3 Resource Filters implementations"""
14+
15+
from cinderclient import api_versions
16+
from osc_lib.command import command
17+
from osc_lib import exceptions
18+
from osc_lib import utils
19+
20+
from openstackclient.i18n import _
21+
22+
23+
class ListBlockStorageResourceFilter(command.Lister):
24+
_description = _('List block storage resource filters')
25+
26+
def take_action(self, parsed_args):
27+
volume_client = self.app.client_manager.volume
28+
29+
if volume_client.api_version < api_versions.APIVersion('3.33'):
30+
msg = _(
31+
"--os-volume-api-version 3.33 or greater is required to "
32+
"support the 'block storage resource filter list' command"
33+
)
34+
raise exceptions.CommandError(msg)
35+
36+
column_headers = (
37+
'Resource',
38+
'Filters',
39+
)
40+
41+
data = volume_client.resource_filters.list()
42+
43+
return (
44+
column_headers,
45+
(utils.get_item_properties(s, column_headers) for s in data)
46+
)
47+
48+
49+
class ShowBlockStorageResourceFilter(command.ShowOne):
50+
_description = _('Show filters for a block storage resource type')
51+
52+
def get_parser(self, prog_name):
53+
parser = super().get_parser(prog_name)
54+
parser.add_argument(
55+
'resource',
56+
metavar='<resource>',
57+
help=_('Resource to show filters for (name).')
58+
)
59+
60+
return parser
61+
62+
def take_action(self, parsed_args):
63+
volume_client = self.app.client_manager.volume
64+
65+
if volume_client.api_version < api_versions.APIVersion('3.33'):
66+
msg = _(
67+
"--os-volume-api-version 3.33 or greater is required to "
68+
"support the 'block storage resource filter show' command"
69+
)
70+
raise exceptions.CommandError(msg)
71+
72+
data = volume_client.resource_filters.list(
73+
resource=parsed_args.resource
74+
)
75+
if not data:
76+
msg = _(
77+
"No resource filter with a name of {parsed_args.resource}' "
78+
"exists."
79+
)
80+
raise exceptions.CommandError(msg)
81+
resource_filter = next(data)
82+
83+
return zip(*sorted(resource_filter._info.items()))

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,8 @@ openstack.volume.v3 =
759759
block_storage_cluster_list = openstackclient.volume.v3.block_storage_cluster:ListBlockStorageCluster
760760
block_storage_cluster_set = openstackclient.volume.v3.block_storage_cluster:SetBlockStorageCluster
761761
block_storage_cluster_show = openstackclient.volume.v3.block_storage_cluster:ShowBlockStorageCluster
762+
block_storage_resource_filter_list = openstackclient.volume.v3.block_storage_resource_filter:ListBlockStorageResourceFilter
763+
block_storage_resource_filter_show = openstackclient.volume.v3.block_storage_resource_filter:ShowBlockStorageResourceFilter
762764

763765
volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot
764766
volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot

0 commit comments

Comments
 (0)