diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv index 068ffb7fa..cc8ef2d91 100644 --- a/doc/source/cli/data/cinder.csv +++ b/doc/source/cli/data/cinder.csv @@ -33,7 +33,7 @@ failover-host,volume host failover,Failover a replicating cinder-volume host. force-delete,volume delete --force,"Attempts force-delete of volume, regardless of state." freeze-host,volume host set --disable,Freeze and disable the specified cinder-volume host. get-capabilities,volume backend capability show,Show capabilities of a volume backend. Admin only. -get-pools,,Show pool information for backends. Admin only. +get-pools,volume backend pool list,Show pool information for backends. Admin only. 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. diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index ad13f1b9c..8d1db63e9 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -252,6 +252,41 @@ class FakeCapability(object): return capability +class FakePool(object): + """Fake Pools.""" + + @staticmethod + def create_one_pool(attrs=None): + """Create a fake pool. + + :param Dictionary attrs: + A dictionary with all attributes of the pool + :return: + A FakeResource object with pool name and attrs. + """ + # Set default attribute + pool_info = { + 'name': 'host@lvmdriver-1#lvmdriver-1', + 'storage_protocol': 'iSCSI', + 'thick_provisioning_support': False, + 'thin_provisioning_support': True, + 'total_volumes': 99, + 'total_capacity_gb': 1000.00, + 'allocated_capacity_gb': 100, + 'max_over_subscription_ratio': 200.0, + } + + # Overwrite default attributes if there are some attributes set + pool_info.update(attrs or {}) + + pool = fakes.FakeResource( + None, + pool_info, + loaded=True) + + return pool + + class FakeVolumeClient(object): def __init__(self, **kwargs): @@ -294,6 +329,8 @@ class FakeVolumeClient(object): self.management_url = kwargs['endpoint'] self.capabilities = mock.Mock() self.capabilities.resource_class = fakes.FakeResource(None, {}) + self.pools = mock.Mock() + self.pools.resource_class = fakes.FakeResource(None, {}) class TestVolume(utils.TestCommand): diff --git a/openstackclient/tests/unit/volume/v2/test_volume_backend.py b/openstackclient/tests/unit/volume/v2/test_volume_backend.py index 73df6032d..db1886608 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume_backend.py +++ b/openstackclient/tests/unit/volume/v2/test_volume_backend.py @@ -71,3 +71,98 @@ class TestShowVolumeCapability(volume_fakes.TestVolume): self.capability_mock.get.assert_called_with( 'fake', ) + + +class TestListVolumePool(volume_fakes.TestVolume): + """Tests for volume backend pool listing.""" + + # The pool to be listed + pools = volume_fakes.FakePool.create_one_pool() + + def setUp(self): + super(TestListVolumePool, self).setUp() + + self.pool_mock = self.app.client_manager.volume.pools + self.pool_mock.list.return_value = [self.pools] + + # Get the command object to test + self.cmd = volume_backend.ListPool(self.app, None) + + def test_pool_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'Name', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.pools.name, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + # checking if proper call was made to list pools + self.pool_mock.list.assert_called_with( + detailed=False, + ) + + # checking if long columns are present in output + self.assertNotIn("total_volumes", columns) + self.assertNotIn("storage_protocol", columns) + + def test_service_list_with_long_option(self): + arglist = [ + '--long' + ] + verifylist = [ + ('long', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + expected_columns = [ + 'Name', + 'Protocol', + 'Thick', + 'Thin', + 'Volumes', + 'Capacity', + 'Allocated', + 'Max Over Ratio', + ] + + # confirming if all expected columns are present in the result. + self.assertEqual(expected_columns, columns) + + datalist = (( + self.pools.name, + self.pools.storage_protocol, + self.pools.thick_provisioning_support, + self.pools.thin_provisioning_support, + self.pools.total_volumes, + self.pools.total_capacity_gb, + self.pools.allocated_capacity_gb, + self.pools.max_over_subscription_ratio, + ), ) + + # confirming if all expected values are present in the result. + self.assertEqual(datalist, tuple(data)) + + self.pool_mock.list.assert_called_with( + detailed=True, + ) diff --git a/openstackclient/volume/v2/volume_backend.py b/openstackclient/volume/v2/volume_backend.py index 0dff18c9a..c5194d350 100644 --- a/openstackclient/volume/v2/volume_backend.py +++ b/openstackclient/volume/v2/volume_backend.py @@ -12,7 +12,7 @@ # under the License. # -"""Capability action implementations""" +"""Storage backend action implementations""" from osc_lib.command import command from osc_lib import utils @@ -59,3 +59,55 @@ class ShowCapability(command.Lister): (utils.get_dict_properties( s, columns, ) for s in print_data)) + + +class ListPool(command.Lister): + _description = _("List pool command") + + def get_parser(self, prog_name): + parser = super(ListPool, self).get_parser(prog_name) + parser.add_argument( + "--long", + action="store_true", + default=False, + help=_("Show detailed information about pools.") + ) + # TODO(smcginnis): Starting with Cinder microversion 3.33, user is also + # able to pass in --filters with a = pair to filter on. + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + + if parsed_args.long: + columns = [ + 'name', + 'storage_protocol', + 'thick_provisioning_support', + 'thin_provisioning_support', + 'total_volumes', + 'total_capacity_gb', + 'allocated_capacity_gb', + 'max_over_subscription_ratio', + ] + headers = [ + 'Name', + 'Protocol', + 'Thick', + 'Thin', + 'Volumes', + 'Capacity', + 'Allocated', + 'Max Over Ratio' + ] + else: + columns = [ + 'Name', + ] + headers = columns + + data = volume_client.pools.list(detailed=parsed_args.long) + return (headers, + (utils.get_item_properties( + s, columns, + ) for s in data)) diff --git a/releasenotes/notes/volume-backend-c5faae0b31556a24.yaml b/releasenotes/notes/volume-backend-c5faae0b31556a24.yaml index 851ab2dec..669830986 100644 --- a/releasenotes/notes/volume-backend-c5faae0b31556a24.yaml +++ b/releasenotes/notes/volume-backend-c5faae0b31556a24.yaml @@ -5,3 +5,7 @@ features: added which will provide a list of all capabilities that can be configured for the requested backend. The required `` parameter takes the form `host@backend-name`. + - | + A new command, ``openstack volume backend pool list`` was added which will + provide a list of all backend storage pools. The optional ``-long`` + parameter includes some basic configuration and stats for each pool. diff --git a/setup.cfg b/setup.cfg index 888e259b2..73c5fde93 100644 --- a/setup.cfg +++ b/setup.cfg @@ -630,6 +630,7 @@ openstack.volume.v2 = volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup volume_backend_capability_show = openstackclient.volume.v2.volume_backend:ShowCapability + volume_backend_pool_list = openstackclient.volume.v2.volume_backend:ListPool volume_host_failover = openstackclient.volume.v2.volume_host:FailoverVolumeHost volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost