From 9647d43bd5e77c815ac2f08b7de2226a657a66bc Mon Sep 17 00:00:00 2001
From: Sean McGinnis <sean.mcginnis@gmail.com>
Date: Mon, 8 Oct 2018 13:17:54 -0500
Subject: [PATCH] Add volume backend pool list command

Adds an equivalent for "cinder get-pools" with "volume backend pool list"
and "cinder get-pools --detail" with "volume backend pool list --long".

Story: 1655624
Task: 136949

Signed-off-by: Sean McGinnis <sean.mcginnis@gmail.com>
Change-Id: I826c9946ffe11340d44ad57914f72fc2a72b6938
---
 doc/source/cli/data/cinder.csv                |  2 +-
 openstackclient/tests/unit/volume/v2/fakes.py | 37 ++++++++
 .../unit/volume/v2/test_volume_backend.py     | 95 +++++++++++++++++++
 openstackclient/volume/v2/volume_backend.py   | 54 ++++++++++-
 .../volume-backend-c5faae0b31556a24.yaml      |  4 +
 setup.cfg                                     |  1 +
 6 files changed, 191 insertions(+), 2 deletions(-)

diff --git a/doc/source/cli/data/cinder.csv b/doc/source/cli/data/cinder.csv
index 068ffb7fac..cc8ef2d91e 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 ad13f1b9cf..8d1db63e9b 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 73df6032de..db18866087 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 0dff18c9ad..c5194d3509 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 <key>=<value> 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 851ab2decd..6698309860 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 `<host>` 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 888e259b22..73c5fde933 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