From 659abf492825b71b99e6f4be58447d6f393e33ef Mon Sep 17 00:00:00 2001
From: chengkunye <chengkun@unitedstack.com>
Date: Fri, 17 Jul 2015 17:05:23 +0800
Subject: [PATCH] Add create and list for volume type v2

Volume API v2 is missing create and list features.

implements bp: volume-v2

Change-Id: I34a1ae440e9620b1c65546f4f43b369c8661250d
---
 doc/source/command-objects/volume-type.rst   |  26 +++-
 openstackclient/tests/volume/v2/test_type.py | 129 +++++++++++++++++++
 openstackclient/volume/v2/volume_type.py     |  97 ++++++++++++++
 setup.cfg                                    |   2 +
 4 files changed, 253 insertions(+), 1 deletion(-)

diff --git a/doc/source/command-objects/volume-type.rst b/doc/source/command-objects/volume-type.rst
index 0898df528b..4d5651ac90 100644
--- a/doc/source/command-objects/volume-type.rst
+++ b/doc/source/command-objects/volume-type.rst
@@ -2,7 +2,7 @@
 volume type
 ===========
 
-Volume v1
+Volume v1, v2
 
 volume type create
 ------------------
@@ -13,9 +13,29 @@ Create new volume type
 .. code:: bash
 
     os volume type create
+        [--description <description>]
+        [--public | --private]
         [--property <key=value> [...] ]
         <name>
 
+.. option:: --description <description>
+
+    New volume type description
+
+    .. versionadded:: 2
+
+.. option:: --public
+
+    Volume type is accessible to the public
+
+    .. versionadded:: 2
+
+.. option:: --private
+
+    Volume type is not accessible to the public
+
+    .. versionadded:: 2
+
 .. option:: --property <key=value>
 
     Set a property on this volume type (repeat option to set multiple properties)
@@ -57,6 +77,8 @@ List volume types
 volume type set
 ---------------
 
+*Only supported for Volume API v1*
+
 Set volume type properties
 
 .. program:: volume type set
@@ -77,6 +99,8 @@ Set volume type properties
 volume type unset
 -----------------
 
+*Only supported for Volume API v1*
+
 Unset volume type properties
 
 .. program:: volume type unset
diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py
index 6cc988b2dc..3963496634 100644
--- a/openstackclient/tests/volume/v2/test_type.py
+++ b/openstackclient/tests/volume/v2/test_type.py
@@ -28,6 +28,135 @@ class TestType(volume_fakes.TestVolume):
         self.types_mock.reset_mock()
 
 
+class TestTypeCreate(TestType):
+
+    def setUp(self):
+        super(TestTypeCreate, self).setUp()
+
+        self.types_mock.create.return_value = fakes.FakeResource(
+            None,
+            copy.deepcopy(volume_fakes.TYPE),
+            loaded=True,
+        )
+        # Get the command object to test
+        self.cmd = volume_type.CreateVolumeType(self.app, None)
+
+    def test_type_create_public(self):
+        arglist = [
+            volume_fakes.type_name,
+            "--description", volume_fakes.type_description,
+            "--public"
+        ]
+        verifylist = [
+            ("name", volume_fakes.type_name),
+            ("description", volume_fakes.type_description),
+            ("public", True),
+            ("private", False),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        self.types_mock.create.assert_called_with(
+            volume_fakes.type_name,
+            description=volume_fakes.type_description,
+            public=True,
+        )
+
+        collist = (
+            'description',
+            'id',
+            'name',
+        )
+        self.assertEqual(collist, columns)
+        datalist = (
+            volume_fakes.type_description,
+            volume_fakes.type_id,
+            volume_fakes.type_name,
+        )
+        self.assertEqual(datalist, data)
+
+    def test_type_create_private(self):
+        arglist = [
+            volume_fakes.type_name,
+            "--description", volume_fakes.type_description,
+            "--private"
+        ]
+        verifylist = [
+            ("name", volume_fakes.type_name),
+            ("description", volume_fakes.type_description),
+            ("public", False),
+            ("private", True),
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        self.types_mock.create.assert_called_with(
+            volume_fakes.type_name,
+            description=volume_fakes.type_description,
+            private=True,
+        )
+
+        collist = (
+            'description',
+            'id',
+            'name',
+        )
+        self.assertEqual(collist, columns)
+        datalist = (
+            volume_fakes.type_description,
+            volume_fakes.type_id,
+            volume_fakes.type_name,
+        )
+        self.assertEqual(datalist, data)
+
+
+class TestTypeList(TestType):
+    def setUp(self):
+        super(TestTypeList, self).setUp()
+
+        self.types_mock.list.return_value = [
+            fakes.FakeResource(
+                None,
+                copy.deepcopy(volume_fakes.TYPE),
+                loaded=True
+            )
+        ]
+        # get the command to test
+        self.cmd = volume_type.ListVolumeType(self.app, None)
+
+    def test_type_list_without_options(self):
+        arglist = []
+        verifylist = [
+            ("long", False)
+        ]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        collist = ["ID", "Name"]
+        self.assertEqual(collist, columns)
+        datalist = ((
+            volume_fakes.type_id,
+            volume_fakes.type_name,
+            ),)
+        self.assertEqual(datalist, tuple(data))
+
+    def test_type_list_with_options(self):
+        arglist = ["--long"]
+        verifylist = [("long", True)]
+        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+        columns, data = self.cmd.take_action(parsed_args)
+        collist = ["ID", "Name", "Description", "Properties"]
+        self.assertEqual(collist, columns)
+        datalist = ((
+            volume_fakes.type_id,
+            volume_fakes.type_name,
+            volume_fakes.type_description,
+            "foo='bar'"
+            ),)
+        self.assertEqual(datalist, tuple(data))
+
+
 class TestTypeShow(TestType):
     def setUp(self):
         super(TestTypeShow, self).setUp()
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index ae5cc8b892..c785021f50 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -17,12 +17,79 @@
 import logging
 
 from cliff import command
+from cliff import lister
 from cliff import show
 import six
 
+from openstackclient.common import parseractions
 from openstackclient.common import utils
 
 
+class CreateVolumeType(show.ShowOne):
+    """Create new volume type"""
+
+    log = logging.getLogger(__name__ + ".CreateVolumeType")
+
+    def get_parser(self, prog_name):
+        parser = super(CreateVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            "name",
+            metavar="<name>",
+            help="New volume type name"
+        )
+        parser.add_argument(
+            "--description",
+            metavar="<description>",
+            help="New volume type description",
+        )
+        public_group = parser.add_mutually_exclusive_group()
+        public_group.add_argument(
+            "--public",
+            dest="public",
+            action="store_true",
+            default=False,
+            help="Volume type is accessible to the public",
+        )
+        public_group.add_argument(
+            "--private",
+            dest="private",
+            action="store_true",
+            default=False,
+            help="Volume type is not accessible to the public",
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this volume type'
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug('take_action(%s)', parsed_args)
+
+        volume_client = self.app.client_manager.volume
+
+        kwargs = {}
+        if parsed_args.public:
+            kwargs['public'] = True
+        if parsed_args.private:
+            kwargs['private'] = True
+
+        volume_type = volume_client.volume_types.create(
+            parsed_args.name,
+            description=parsed_args.description,
+            **kwargs
+        )
+        volume_type._info.pop('extra_specs')
+        if parsed_args.property:
+            result = volume_type.set_keys(parsed_args.property)
+            volume_type._info.update({'properties': utils.format_dict(result)})
+
+        return zip(*sorted(six.iteritems(volume_type._info)))
+
+
 class DeleteVolumeType(command.Command):
     """Delete volume type"""
 
@@ -46,6 +113,36 @@ class DeleteVolumeType(command.Command):
         return
 
 
+class ListVolumeType(lister.Lister):
+    """List volume types"""
+
+    log = logging.getLogger(__name__ + '.ListVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(ListVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help='List additional fields in output')
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug('take_action(%s)', parsed_args)
+        if parsed_args.long:
+            columns = ['ID', 'Name', 'Description', 'Extra Specs']
+            column_headers = ['ID', 'Name', 'Description', 'Properties']
+        else:
+            columns = ['ID', 'Name']
+            column_headers = columns
+        data = self.app.client_manager.volume.volume_types.list()
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={'Extra Specs': utils.format_dict},
+                ) for s in data))
+
+
 class ShowVolumeType(show.ShowOne):
     """Display volume type details"""
 
diff --git a/setup.cfg b/setup.cfg
index 826605738b..d73a09ec9b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -393,7 +393,9 @@ openstack.volume.v2 =
     volume_delete = openstackclient.volume.v2.volume:DeleteVolume
     volume_show = openstackclient.volume.v2.volume:ShowVolume
 
+    volume_type_create = openstackclient.volume.v2.volume_type:CreateVolumeType
     volume_type_delete = openstackclient.volume.v2.volume_type:DeleteVolumeType
+    volume_type_list = openstackclient.volume.v2.volume_type:ListVolumeType
     volume_type_show = openstackclient.volume.v2.volume_type:ShowVolumeType
 
     volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos