diff --git a/doc/source/command-objects/volume.rst b/doc/source/command-objects/volume.rst index a51d1117d..021518be7 100644 --- a/doc/source/command-objects/volume.rst +++ b/doc/source/command-objects/volume.rst @@ -88,13 +88,19 @@ Delete volume(s) .. code:: bash os volume delete - [--force] + [--force | --purge] [ ...] .. option:: --force Attempt forced removal of volume(s), regardless of state (defaults to False) +.. option:: --purge + + Remove any snapshots along with volume(s) (defaults to False) + + *Volume version 2 only* + .. _volume_delete-volume: .. describe:: diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py index 25d0e92fe..6f552ad61 100644 --- a/openstackclient/tests/volume/v2/test_volume.py +++ b/openstackclient/tests/volume/v2/test_volume.py @@ -420,13 +420,16 @@ class TestVolumeDelete(TestVolume): volumes[0].id ] verifylist = [ - ("volumes", [volumes[0].id]) + ("force", False), + ("purge", False), + ("volumes", [volumes[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - self.volumes_mock.delete.assert_called_with(volumes[0].id) + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=False) self.assertIsNone(result) def test_volume_delete_multi_volumes(self): @@ -434,13 +437,15 @@ class TestVolumeDelete(TestVolume): arglist = [v.id for v in volumes] verifylist = [ + ('force', False), + ('purge', False), ('volumes', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) - calls = [call(v.id) for v in volumes] + calls = [call(v.id, cascade=False) for v in volumes] self.volumes_mock.delete.assert_has_calls(calls) self.assertIsNone(result) @@ -452,6 +457,8 @@ class TestVolumeDelete(TestVolume): 'unexist_volume', ] verifylist = [ + ('force', False), + ('purge', False), ('volumes', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -471,8 +478,46 @@ class TestVolumeDelete(TestVolume): self.assertEqual(2, find_mock.call_count) self.volumes_mock.delete.assert_called_once_with( - volumes[0].id - ) + volumes[0].id, cascade=False) + + def test_volume_delete_with_purge(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + '--purge', + volumes[0].id, + ] + verifylist = [ + ('force', False), + ('purge', True), + ('volumes', [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.delete.assert_called_once_with( + volumes[0].id, cascade=True) + self.assertIsNone(result) + + def test_volume_delete_with_force(self): + volumes = self.setup_volumes_mock(count=1) + + arglist = [ + '--force', + volumes[0].id, + ] + verifylist = [ + ('force', True), + ('purge', False), + ('volumes', [volumes[0].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.volumes_mock.force_delete.assert_called_once_with(volumes[0].id) + self.assertIsNone(result) class TestVolumeList(TestVolume): diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index 85f267ef5..6f055922f 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -166,13 +166,19 @@ class DeleteVolume(command.Command): nargs="+", help=_("Volume(s) to delete (name or ID)") ) - parser.add_argument( + group = parser.add_mutually_exclusive_group() + group.add_argument( "--force", action="store_true", - default=False, help=_("Attempt forced removal of volume(s), regardless of state " "(defaults to False)") ) + group.add_argument( + "--purge", + action="store_true", + help=_("Remove any snapshots along with volume(s) " + "(defaults to False)") + ) return parser def take_action(self, parsed_args): @@ -186,12 +192,13 @@ class DeleteVolume(command.Command): if parsed_args.force: volume_client.volumes.force_delete(volume_obj.id) else: - volume_client.volumes.delete(volume_obj.id) + volume_client.volumes.delete(volume_obj.id, + cascade=parsed_args.purge) except Exception as e: result += 1 LOG.error(_("Failed to delete volume with " - "name or ID '%(volume)s': %(e)s") - % {'volume': i, 'e': e}) + "name or ID '%(volume)s': %(e)s"), + {'volume': i, 'e': e}) if result > 0: total = len(parsed_args.volumes) diff --git a/releasenotes/notes/bug-1589332-2941f5286df7e5d4.yaml b/releasenotes/notes/bug-1589332-2941f5286df7e5d4.yaml new file mode 100644 index 000000000..0ac17c39c --- /dev/null +++ b/releasenotes/notes/bug-1589332-2941f5286df7e5d4.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add ``--purge`` option to ``volume delete`` command (Volume v2 only) in + order to removing any snapshots along with volume automatically when user + delete the volume. + [Bug `1589332 `_]