From 4e62e1e2e18cb93ba0f88bff8727182b1002de4b Mon Sep 17 00:00:00 2001
From: Steve Martinelli <s.martinelli@gmail.com>
Date: Thu, 16 Jun 2016 14:25:33 -0400
Subject: [PATCH] support multi-delete for volume-type

Added the ability to delete multiple volume types at once. Note
there are no unit tests exist for v1 volume-types, so instead
a functional test was created.

Partial-Bug: #1592906
Change-Id: I99f3f22901ab35252b91a3072b14de7d19cb17ca
---
 doc/source/command-objects/volume-type.rst    |  6 ++--
 .../tests/volume/v1/test_volume_type.py       | 13 +++++++
 .../tests/volume/v2/test_volume_type.py       | 13 +++++++
 openstackclient/tests/volume/v2/test_type.py  |  4 +--
 openstackclient/volume/v1/volume_type.py      | 35 +++++++++++++++----
 openstackclient/volume/v2/volume_type.py      | 29 +++++++++++----
 .../notes/bug-1592906-e69b37377278d9c2.yaml   |  5 +++
 7 files changed, 88 insertions(+), 17 deletions(-)
 create mode 100644 releasenotes/notes/bug-1592906-e69b37377278d9c2.yaml

diff --git a/doc/source/command-objects/volume-type.rst b/doc/source/command-objects/volume-type.rst
index 50acf9fa65..dfc169cd14 100644
--- a/doc/source/command-objects/volume-type.rst
+++ b/doc/source/command-objects/volume-type.rst
@@ -48,18 +48,18 @@ Create new volume type
 volume type delete
 ------------------
 
-Delete volume type
+Delete volume type(s)
 
 .. program:: volume type delete
 .. code:: bash
 
     os volume type delete
-        <volume-type>
+        <volume-type> [<volume-type> ...]
 
 .. _volume_type_delete-volume-type:
 .. describe:: <volume-type>
 
-    Volume type to delete (name or ID)
+    Volume type(s) to delete (name or ID)
 
 volume type list
 ----------------
diff --git a/functional/tests/volume/v1/test_volume_type.py b/functional/tests/volume/v1/test_volume_type.py
index 824b20d7c4..5f1f957e8c 100644
--- a/functional/tests/volume/v1/test_volume_type.py
+++ b/functional/tests/volume/v1/test_volume_type.py
@@ -10,6 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
 import uuid
 
 from functional.tests.volume.v1 import common
@@ -59,3 +60,15 @@ class VolumeTypeTests(common.BaseVolumeTests):
         self.assertEqual("", raw_output)
         raw_output = self.openstack('volume type show ' + self.NAME + opts)
         self.assertEqual("c='d'\n", raw_output)
+
+    def test_multi_delete(self):
+        vol_type1 = uuid.uuid4().hex
+        vol_type2 = uuid.uuid4().hex
+        self.openstack('volume type create ' + vol_type1)
+        time.sleep(5)
+        self.openstack('volume type create ' + vol_type2)
+        time.sleep(5)
+        cmd = 'volume type delete %s %s' % (vol_type1, vol_type2)
+        time.sleep(5)
+        raw_output = self.openstack(cmd)
+        self.assertOutput('', raw_output)
diff --git a/functional/tests/volume/v2/test_volume_type.py b/functional/tests/volume/v2/test_volume_type.py
index 4c315334c8..114e429802 100644
--- a/functional/tests/volume/v2/test_volume_type.py
+++ b/functional/tests/volume/v2/test_volume_type.py
@@ -10,6 +10,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import time
 import uuid
 
 from functional.tests.volume.v2 import common
@@ -69,3 +70,15 @@ class VolumeTypeTests(common.BaseVolumeTests):
         raw_output = self.openstack(
             'volume type unset --project admin ' + self.NAME)
         self.assertEqual("", raw_output)
+
+    def test_multi_delete(self):
+        vol_type1 = uuid.uuid4().hex
+        vol_type2 = uuid.uuid4().hex
+        self.openstack('volume type create ' + vol_type1)
+        time.sleep(5)
+        self.openstack('volume type create ' + vol_type2)
+        time.sleep(5)
+        cmd = 'volume type delete %s %s' % (vol_type1, vol_type2)
+        time.sleep(5)
+        raw_output = self.openstack(cmd)
+        self.assertOutput('', raw_output)
diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py
index 7eea1c3c45..174f33f2fb 100644
--- a/openstackclient/tests/volume/v2/test_type.py
+++ b/openstackclient/tests/volume/v2/test_type.py
@@ -128,13 +128,13 @@ class TestTypeDelete(TestType):
             self.volume_type.id
         ]
         verifylist = [
-            ("volume_type", self.volume_type.id)
+            ("volume_types", [self.volume_type.id])
         ]
         parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 
         result = self.cmd.take_action(parsed_args)
 
-        self.types_mock.delete.assert_called_with(self.volume_type.id)
+        self.types_mock.delete.assert_called_with(self.volume_type)
         self.assertIsNone(result)
 
 
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index 289966e52e..3fe4fa05f1 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -15,14 +15,20 @@
 
 """Volume v1 Type action implementations"""
 
+import logging
+
 from osc_lib.cli import parseractions
 from osc_lib.command import command
+from osc_lib import exceptions
 from osc_lib import utils
 import six
 
 from openstackclient.i18n import _
 
 
+LOG = logging.getLogger(__name__)
+
+
 class CreateVolumeType(command.ShowOne):
     """Create new volume type"""
 
@@ -56,22 +62,39 @@ class CreateVolumeType(command.ShowOne):
 
 
 class DeleteVolumeType(command.Command):
-    """Delete volume type"""
+    """Delete volume type(s)"""
 
     def get_parser(self, prog_name):
         parser = super(DeleteVolumeType, self).get_parser(prog_name)
         parser.add_argument(
-            'volume_type',
+            'volume_types',
             metavar='<volume-type>',
-            help=_('Volume type to delete (name or ID)'),
+            nargs='+',
+            help=_('Volume type(s) to delete (name or ID)'),
         )
         return parser
 
     def take_action(self, parsed_args):
         volume_client = self.app.client_manager.volume
-        volume_type_id = utils.find_resource(
-            volume_client.volume_types, parsed_args.volume_type).id
-        volume_client.volume_types.delete(volume_type_id)
+        result = 0
+
+        for volume_type in parsed_args.volume_types:
+            try:
+                vol_type = utils.find_resource(volume_client.volume_types,
+                                               volume_type)
+
+                volume_client.volume_types.delete(vol_type)
+            except Exception as e:
+                result += 1
+                LOG.error(_("Failed to delete volume type with "
+                            "name or ID '%(volume_type)s': %(e)s")
+                          % {'volume_type': volume_type, 'e': e})
+
+        if result > 0:
+            total = len(parsed_args.volume_types)
+            msg = (_("%(result)s of %(total)s volume types failed "
+                   "to delete.") % {'result': result, 'total': total})
+            raise exceptions.CommandError(msg)
 
 
 class ListVolumeType(command.Lister):
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 5e9faa1ded..87f4c5472a 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -92,22 +92,39 @@ class CreateVolumeType(command.ShowOne):
 
 
 class DeleteVolumeType(command.Command):
-    """Delete volume type"""
+    """Delete volume type(s)"""
 
     def get_parser(self, prog_name):
         parser = super(DeleteVolumeType, self).get_parser(prog_name)
         parser.add_argument(
-            "volume_type",
+            "volume_types",
             metavar="<volume-type>",
-            help=_("Volume type to delete (name or ID)")
+            nargs="+",
+            help=_("Volume type(s) to delete (name or ID)")
         )
         return parser
 
     def take_action(self, parsed_args):
         volume_client = self.app.client_manager.volume
-        volume_type = utils.find_resource(
-            volume_client.volume_types, parsed_args.volume_type)
-        volume_client.volume_types.delete(volume_type.id)
+        result = 0
+
+        for volume_type in parsed_args.volume_types:
+            try:
+                vol_type = utils.find_resource(volume_client.volume_types,
+                                               volume_type)
+
+                volume_client.volume_types.delete(vol_type)
+            except Exception as e:
+                result += 1
+                LOG.error(_("Failed to delete volume type with "
+                            "name or ID '%(volume_type)s': %(e)s")
+                          % {'volume_type': volume_type, 'e': e})
+
+        if result > 0:
+            total = len(parsed_args.volume_types)
+            msg = (_("%(result)s of %(total)s volume types failed "
+                   "to delete.") % {'result': result, 'total': total})
+            raise exceptions.CommandError(msg)
 
 
 class ListVolumeType(command.Lister):
diff --git a/releasenotes/notes/bug-1592906-e69b37377278d9c2.yaml b/releasenotes/notes/bug-1592906-e69b37377278d9c2.yaml
new file mode 100644
index 0000000000..fee971d6dd
--- /dev/null
+++ b/releasenotes/notes/bug-1592906-e69b37377278d9c2.yaml
@@ -0,0 +1,5 @@
+---
+features:
+  - Support bulk deletion for ``volume type delete``.
+    [Bug `1592906 <https://bugs.launchpad.net/bugs/1592906>`_]
+