summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorwhoami-rajat <rajatdhasmana@gmail.com>2023-02-04 00:51:47 +0530
committerwhoami-rajat <rajatdhasmana@gmail.com>2023-02-17 10:03:01 +0000
commit73b4ce88eb3e71568cf5330d6c0a8a4e92287d89 (patch)
tree037c09a3679f875d4ad851ee4af4479d60a2993c
parent2be359677908b81d9985917fd78f9c6897759fb9 (diff)
downloadpython-openstackclient-73b4ce88eb3e71568cf5330d6c0a8a4e92287d89.tar.gz
Add block storage manageable list commands
This patch adds the ``block storage volume manageable list`` and ``block storage snapshot manageable list`` commands that allow operators to list the volumes and snapshots on a particular host or cluster for management under OpenStack. Change-Id: I328dada5a0dc4e9e44c0d51db5cf3f224e27f88f
-rw-r--r--doc/source/cli/command-objects/block-storage-manage.rst11
-rw-r--r--doc/source/cli/data/cinder.csv4
-rw-r--r--openstackclient/tests/unit/volume/v3/fakes.py38
-rw-r--r--openstackclient/tests/unit/volume/v3/test_block_storage_manage.py411
-rw-r--r--openstackclient/volume/v3/block_storage_manage.py258
-rw-r--r--releasenotes/notes/add-block-storage-manage-commands-6ebf029bd7a67bb3.yaml7
-rw-r--r--setup.cfg2
7 files changed, 729 insertions, 2 deletions
diff --git a/doc/source/cli/command-objects/block-storage-manage.rst b/doc/source/cli/command-objects/block-storage-manage.rst
new file mode 100644
index 00000000..a1cff1ad
--- /dev/null
+++ b/doc/source/cli/command-objects/block-storage-manage.rst
@@ -0,0 +1,11 @@
+====================
+Block Storage Manage
+====================
+
+Block Storage v3
+
+.. autoprogram-cliff:: openstack.volume.v3
+ :command: block storage volume manageable list
+
+.. autoprogram-cliff:: openstack.volume.v3
+ :command: block storage snapshot manageable list
diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv
index ebf57221..d5dc42c1 100644
--- a/doc/source/cli/data/cinder.csv
+++ b/doc/source/cli/data/cinder.csv
@@ -71,7 +71,7 @@ image-metadata-show,volume show,Shows volume image metadata.
list,volume list,Lists all volumes.
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)
+manageable-list,block storage 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)
message-list,volume message list,Lists all messages. (Supported by API versions 3.3 - 3.latest)
message-show,volume message show,Shows message details. (Supported by API versions 3.3 - 3.latest)
@@ -112,7 +112,7 @@ snapshot-create,snapshot create,Creates a snapshot.
snapshot-delete,snapshot delete,Remove one or more snapshots.
snapshot-list,snapshot list,Lists all snapshots.
snapshot-manage,volume snapshot create --remote-source <key=value>,Manage an existing snapshot.
-snapshot-manageable-list,,Lists all manageable snapshots. (Supported by API versions 3.8 - 3.latest)
+snapshot-manageable-list,block storage snapshot manageable list,Lists all manageable snapshots. (Supported by API versions 3.8 - 3.latest)
snapshot-metadata,snapshot set --property k=v / snapshot unset --property k,Sets or deletes snapshot metadata.
snapshot-metadata-show,snapshot show,Shows snapshot metadata.
snapshot-metadata-update-all,snapshot set --property k=v,Updates snapshot metadata.
diff --git a/openstackclient/tests/unit/volume/v3/fakes.py b/openstackclient/tests/unit/volume/v3/fakes.py
index 7bde154e..62383580 100644
--- a/openstackclient/tests/unit/volume/v3/fakes.py
+++ b/openstackclient/tests/unit/volume/v3/fakes.py
@@ -487,3 +487,41 @@ def create_cleanup_records():
None, obj, loaded=True) for obj in unavailable_records]
return cleaning, unavailable
+
+
+def create_one_manage_record(attrs=None, snapshot=False):
+ manage_dict = {
+ 'reference': {'source-name': 'fake-volume'},
+ 'size': '1',
+ 'safe_to_manage': False,
+ 'reason_not_safe': 'already managed',
+ 'cinder_id': 'fake-volume',
+ 'extra_info': None,
+ }
+ if snapshot:
+ manage_dict['source_reference'] = {'source-name': 'fake-source'}
+
+ # Overwrite default attributes if there are some attributes set
+ attrs = attrs or {}
+
+ manage_dict.update(attrs)
+ manage_record = fakes.FakeResource(None, manage_dict, loaded=True)
+ return manage_record
+
+
+def create_volume_manage_list_records(count=2):
+ volume_manage_list = []
+ for i in range(count):
+ volume_manage_list.append(
+ create_one_manage_record({'size': str(i + 1)}))
+
+ return volume_manage_list
+
+
+def create_snapshot_manage_list_records(count=2):
+ snapshot_manage_list = []
+ for i in range(count):
+ snapshot_manage_list.append(
+ create_one_manage_record({'size': str(i + 1)}, snapshot=True))
+
+ return snapshot_manage_list
diff --git a/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py
new file mode 100644
index 00000000..afd0fd35
--- /dev/null
+++ b/openstackclient/tests/unit/volume/v3/test_block_storage_manage.py
@@ -0,0 +1,411 @@
+# 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 import utils as tests_utils
+from openstackclient.tests.unit.volume.v2 import fakes as v2_volume_fakes
+from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes
+from openstackclient.volume.v3 import block_storage_manage
+
+
+class TestBlockStorageManage(v2_volume_fakes.TestVolume):
+
+ def setUp(self):
+ super().setUp()
+
+ self.volumes_mock = self.app.client_manager.volume.volumes
+ self.volumes_mock.reset_mock()
+ self.snapshots_mock = self.app.client_manager.volume.volume_snapshots
+ self.snapshots_mock.reset_mock()
+
+
+class TestBlockStorageVolumeManage(TestBlockStorageManage):
+
+ volume_manage_list = volume_fakes.create_volume_manage_list_records()
+
+ def setUp(self):
+ super().setUp()
+
+ self.volumes_mock.list_manageable.return_value = (
+ self.volume_manage_list)
+
+ # Get the command object to test
+ self.cmd = block_storage_manage.BlockStorageManageVolumes(
+ self.app, None)
+
+ def test_block_storage_volume_manage_list(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.8')
+ host = 'fake_host'
+ arglist = [
+ host,
+ ]
+ verifylist = [
+ ('host', host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'reference',
+ 'size',
+ 'safe_to_manage',
+ 'reason_not_safe',
+ 'cinder_id',
+ 'extra_info',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = []
+ for volume_record in self.volume_manage_list:
+ manage_details = (
+ volume_record.reference,
+ volume_record.size,
+ volume_record.safe_to_manage,
+ volume_record.reason_not_safe,
+ volume_record.cinder_id,
+ volume_record.extra_info,
+ )
+ datalist.append(manage_details)
+ datalist = tuple(datalist)
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to get volume manageable list
+ self.volumes_mock.list_manageable.assert_called_with(
+ host=parsed_args.host,
+ detailed=parsed_args.detailed,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit,
+ offset=parsed_args.offset,
+ sort=parsed_args.sort,
+ cluster=parsed_args.cluster,
+ )
+
+ def test_block_storage_volume_manage_pre_38(self):
+ host = 'fake_host'
+ arglist = [
+ host,
+ ]
+ verifylist = [
+ ('host', host),
+ ]
+ 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.8 or greater is required', str(exc))
+
+ def test_block_storage_volume_manage_pre_317(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.16')
+ cluster = 'fake_cluster'
+ arglist = [
+ '--cluster', cluster,
+ ]
+ verifylist = [
+ ('cluster', cluster),
+ ]
+ 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.17 or greater is required', str(exc))
+ self.assertIn('--cluster', str(exc))
+
+ def test_block_storage_volume_manage_host_and_cluster(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.17')
+ host = 'fake_host'
+ cluster = 'fake_cluster'
+ arglist = [
+ host,
+ '--cluster', cluster,
+ ]
+ verifylist = [
+ ('host', host),
+ ('cluster', cluster),
+ ]
+ exc = self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd,
+ arglist, verifylist)
+ self.assertIn(
+ 'argument --cluster: not allowed with argument <host>', str(exc))
+
+ def test_block_storage_volume_manage_list_all_args(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.8')
+ host = 'fake_host'
+ detailed = True
+ marker = 'fake_marker'
+ limit = '5'
+ offset = '3'
+ sort = 'size:asc'
+ arglist = [
+ host,
+ '--detailed', str(detailed),
+ '--marker', marker,
+ '--limit', limit,
+ '--offset', offset,
+ '--sort', sort,
+ ]
+ verifylist = [
+ ('host', host),
+ ('detailed', str(detailed)),
+ ('marker', marker),
+ ('limit', limit),
+ ('offset', offset),
+ ('sort', sort),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'reference',
+ 'size',
+ 'safe_to_manage',
+ 'reason_not_safe',
+ 'cinder_id',
+ 'extra_info',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = []
+ for volume_record in self.volume_manage_list:
+ manage_details = (
+ volume_record.reference,
+ volume_record.size,
+ volume_record.safe_to_manage,
+ volume_record.reason_not_safe,
+ volume_record.cinder_id,
+ volume_record.extra_info,
+ )
+ datalist.append(manage_details)
+ datalist = tuple(datalist)
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to get volume manageable list
+ self.volumes_mock.list_manageable.assert_called_with(
+ host=host,
+ detailed=detailed,
+ marker=marker,
+ limit=limit,
+ offset=offset,
+ sort=sort,
+ cluster=parsed_args.cluster,
+ )
+
+
+class TestBlockStorageSnapshotManage(TestBlockStorageManage):
+
+ snapshot_manage_list = volume_fakes.create_snapshot_manage_list_records()
+
+ def setUp(self):
+ super().setUp()
+
+ self.snapshots_mock.list_manageable.return_value = (
+ self.snapshot_manage_list)
+
+ # Get the command object to test
+ self.cmd = block_storage_manage.BlockStorageManageSnapshots(
+ self.app, None)
+
+ def test_block_storage_snapshot_manage_list(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.8')
+ host = 'fake_host'
+ arglist = [
+ host,
+ ]
+ verifylist = [
+ ('host', host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'reference',
+ 'size',
+ 'safe_to_manage',
+ 'source_reference',
+ 'reason_not_safe',
+ 'cinder_id',
+ 'extra_info',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = []
+ for snapshot_record in self.snapshot_manage_list:
+ manage_details = (
+ snapshot_record.reference,
+ snapshot_record.size,
+ snapshot_record.safe_to_manage,
+ snapshot_record.source_reference,
+ snapshot_record.reason_not_safe,
+ snapshot_record.cinder_id,
+ snapshot_record.extra_info,
+ )
+ datalist.append(manage_details)
+ datalist = tuple(datalist)
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to get snapshot manageable list
+ self.snapshots_mock.list_manageable.assert_called_with(
+ host=parsed_args.host,
+ detailed=parsed_args.detailed,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit,
+ offset=parsed_args.offset,
+ sort=parsed_args.sort,
+ cluster=parsed_args.cluster,
+ )
+
+ def test_block_storage_volume_manage_pre_38(self):
+ host = 'fake_host'
+ arglist = [
+ host,
+ ]
+ verifylist = [
+ ('host', host),
+ ]
+ 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.8 or greater is required', str(exc))
+
+ def test_block_storage_volume_manage_pre_317(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.16')
+ cluster = 'fake_cluster'
+ arglist = [
+ '--cluster', cluster,
+ ]
+ verifylist = [
+ ('cluster', cluster),
+ ]
+ 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.17 or greater is required', str(exc))
+ self.assertIn('--cluster', str(exc))
+
+ def test_block_storage_volume_manage_host_and_cluster(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.17')
+ host = 'fake_host'
+ cluster = 'fake_cluster'
+ arglist = [
+ host,
+ '--cluster', cluster,
+ ]
+ verifylist = [
+ ('host', host),
+ ('cluster', cluster),
+ ]
+ exc = self.assertRaises(tests_utils.ParserException,
+ self.check_parser, self.cmd,
+ arglist, verifylist)
+ self.assertIn(
+ 'argument --cluster: not allowed with argument <host>', str(exc))
+
+ def test_block_storage_volume_manage_list_all_args(self):
+ self.app.client_manager.volume.api_version = \
+ api_versions.APIVersion('3.8')
+ host = 'fake_host'
+ detailed = True
+ marker = 'fake_marker'
+ limit = '5'
+ offset = '3'
+ sort = 'size:asc'
+ arglist = [
+ host,
+ '--detailed', str(detailed),
+ '--marker', marker,
+ '--limit', limit,
+ '--offset', offset,
+ '--sort', sort,
+ ]
+ verifylist = [
+ ('host', host),
+ ('detailed', str(detailed)),
+ ('marker', marker),
+ ('limit', limit),
+ ('offset', offset),
+ ('sort', sort),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'reference',
+ 'size',
+ 'safe_to_manage',
+ 'source_reference',
+ 'reason_not_safe',
+ 'cinder_id',
+ 'extra_info',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = []
+ for snapshot_record in self.snapshot_manage_list:
+ manage_details = (
+ snapshot_record.reference,
+ snapshot_record.size,
+ snapshot_record.safe_to_manage,
+ snapshot_record.source_reference,
+ snapshot_record.reason_not_safe,
+ snapshot_record.cinder_id,
+ snapshot_record.extra_info,
+ )
+ datalist.append(manage_details)
+ datalist = tuple(datalist)
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to get snapshot manageable list
+ self.snapshots_mock.list_manageable.assert_called_with(
+ host=host,
+ detailed=detailed,
+ marker=marker,
+ limit=limit,
+ offset=offset,
+ sort=sort,
+ cluster=parsed_args.cluster,
+ )
diff --git a/openstackclient/volume/v3/block_storage_manage.py b/openstackclient/volume/v3/block_storage_manage.py
new file mode 100644
index 00000000..9015f44d
--- /dev/null
+++ b/openstackclient/volume/v3/block_storage_manage.py
@@ -0,0 +1,258 @@
+# 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.
+#
+
+"""Block Storage Volume/Snapshot Management implementations"""
+
+from cinderclient import api_versions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+from oslo_utils import strutils
+
+from openstackclient.i18n import _
+
+
+SORT_MANAGEABLE_KEY_VALUES = ('size', 'reference')
+
+
+class BlockStorageManageVolumes(command.Lister):
+ """List manageable volumes.
+
+ Supported by --os-volume-api-version 3.8 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ host_group = parser.add_mutually_exclusive_group()
+ host_group.add_argument(
+ "host",
+ metavar="<host>",
+ nargs='?',
+ help=_('Cinder host on which to list manageable volumes. '
+ 'Takes the form: host@backend-name#pool')
+ )
+ host_group.add_argument(
+ "--cluster",
+ metavar="<cluster>",
+ help=_('Cinder cluster on which to list manageable volumes. '
+ 'Takes the form: cluster@backend-name#pool. '
+ '(supported by --os-volume-api-version 3.17 or later)')
+ )
+ parser.add_argument(
+ '--detailed',
+ metavar='<detailed>',
+ default=True,
+ help=_('Returns detailed information (Default=True).')
+ )
+ parser.add_argument(
+ '--marker',
+ metavar='<marker>',
+ default=None,
+ help=_('Begin returning volumes that appear later in the volume '
+ 'list than that represented by this reference. This '
+ 'reference should be json like. Default=None.')
+ )
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ default=None,
+ help=_('Maximum number of volumes to return. Default=None.')
+ )
+ parser.add_argument(
+ '--offset',
+ metavar='<offset>',
+ default=None,
+ help=_('Number of volumes to skip after marker. Default=None.')
+ )
+ parser.add_argument(
+ '--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(_('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ 'Valid keys: %s. '
+ 'Default=None.') % ', '.join(SORT_MANAGEABLE_KEY_VALUES))
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if parsed_args.host is None and parsed_args.cluster is None:
+ msg = _(
+ "Either <host> or '--cluster <cluster>' needs to be provided "
+ "to run the 'block storage volume manageable list' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ if volume_client.api_version < api_versions.APIVersion('3.8'):
+ msg = _(
+ "--os-volume-api-version 3.8 or greater is required to "
+ "support the 'block storage volume manageable list' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ if parsed_args.cluster:
+ if volume_client.api_version < api_versions.APIVersion('3.17'):
+ msg = _(
+ "--os-volume-api-version 3.17 or greater is required to "
+ "support the '--cluster' option"
+ )
+ raise exceptions.CommandError(msg)
+
+ detailed = strutils.bool_from_string(parsed_args.detailed)
+ cluster = getattr(parsed_args, 'cluster', None)
+
+ columns = [
+ 'reference',
+ 'size',
+ 'safe_to_manage',
+ ]
+ if detailed:
+ columns.extend([
+ 'reason_not_safe',
+ 'cinder_id',
+ 'extra_info',
+ ])
+
+ data = volume_client.volumes.list_manageable(
+ host=parsed_args.host,
+ detailed=detailed,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit,
+ offset=parsed_args.offset,
+ sort=parsed_args.sort,
+ cluster=cluster)
+
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))
+
+
+class BlockStorageManageSnapshots(command.Lister):
+ """List manageable snapshots.
+
+ Supported by --os-volume-api-version 3.8 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ host_group = parser.add_mutually_exclusive_group()
+ host_group.add_argument(
+ "host",
+ metavar="<host>",
+ nargs='?',
+ help=_('Cinder host on which to list manageable snapshots. '
+ 'Takes the form: host@backend-name#pool')
+ )
+ host_group.add_argument(
+ "--cluster",
+ metavar="<cluster>",
+ help=_('Cinder cluster on which to list manageable snapshots. '
+ 'Takes the form: cluster@backend-name#pool. '
+ '(supported by --os-volume-api-version 3.17 or later)')
+ )
+ parser.add_argument(
+ '--detailed',
+ metavar='<detailed>',
+ default=True,
+ help=_('Returns detailed information (Default=True).')
+ )
+ parser.add_argument(
+ '--marker',
+ metavar='<marker>',
+ default=None,
+ help=_('Begin returning snapshots that appear later in the '
+ 'snapshot list than that represented by this reference. '
+ 'This reference should be json like. Default=None.')
+ )
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ default=None,
+ help=_('Maximum number of snapshots to return. Default=None.')
+ )
+ parser.add_argument(
+ '--offset',
+ metavar='<offset>',
+ default=None,
+ help=_('Number of snapshots to skip after marker. Default=None.')
+ )
+ parser.add_argument(
+ '--sort',
+ metavar='<key>[:<direction>]',
+ default=None,
+ help=(_('Comma-separated list of sort keys and directions in the '
+ 'form of <key>[:<asc|desc>]. '
+ 'Valid keys: %s. '
+ 'Default=None.') % ', '.join(SORT_MANAGEABLE_KEY_VALUES))
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if parsed_args.host is None and parsed_args.cluster is None:
+ msg = _(
+ "Either <host> or '--cluster <cluster>' needs to be provided "
+ "to run the 'block storage volume snapshot manageable list' "
+ "command"
+ )
+ raise exceptions.CommandError(msg)
+
+ if volume_client.api_version < api_versions.APIVersion('3.8'):
+ msg = _(
+ "--os-volume-api-version 3.8 or greater is required to "
+ "support the 'block storage volume snapshot manageable list' "
+ "command"
+ )
+ raise exceptions.CommandError(msg)
+
+ if parsed_args.cluster:
+ if volume_client.api_version < api_versions.APIVersion('3.17'):
+ msg = _(
+ "--os-volume-api-version 3.17 or greater is required to "
+ "support the '--cluster' option"
+ )
+ raise exceptions.CommandError(msg)
+
+ detailed = strutils.bool_from_string(parsed_args.detailed)
+ cluster = getattr(parsed_args, 'cluster', None)
+
+ columns = [
+ 'reference',
+ 'size',
+ 'safe_to_manage',
+ 'source_reference',
+ ]
+ if detailed:
+ columns.extend([
+ 'reason_not_safe',
+ 'cinder_id',
+ 'extra_info',
+ ])
+
+ data = volume_client.volume_snapshots.list_manageable(
+ host=parsed_args.host,
+ detailed=detailed,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit,
+ offset=parsed_args.offset,
+ sort=parsed_args.sort,
+ cluster=cluster)
+
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))
diff --git a/releasenotes/notes/add-block-storage-manage-commands-6ebf029bd7a67bb3.yaml b/releasenotes/notes/add-block-storage-manage-commands-6ebf029bd7a67bb3.yaml
new file mode 100644
index 00000000..7b40a341
--- /dev/null
+++ b/releasenotes/notes/add-block-storage-manage-commands-6ebf029bd7a67bb3.yaml
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Added ``block storage volume manageable list`` and
+ ``block storage snapshot manageable list`` commands that
+ allow operators to list the volumes and snapshots on a
+ particular host or cluster for management under OpenStack.
diff --git a/setup.cfg b/setup.cfg
index 7ee9f489..c3c99ccd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -825,3 +825,5 @@ openstack.volume.v3 =
block_storage_log_level_list = openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelList
block_storage_log_level_set = openstackclient.volume.v3.block_storage_log_level:BlockStorageLogLevelSet
block_storage_cleanup = openstackclient.volume.v3.block_storage_cleanup:BlockStorageCleanup
+ block_storage_volume_manageable_list = openstackclient.volume.v3.block_storage_manage:BlockStorageManageVolumes
+ block_storage_snapshot_manageable_list = openstackclient.volume.v3.block_storage_manage:BlockStorageManageSnapshots