diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index fd5eca380..e90a436f5 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -4762,12 +4762,15 @@ class OpenStackCloud( return self._normalize_volume(volume) - def delete_volume(self, name_or_id=None, wait=True, timeout=None): + def delete_volume(self, name_or_id=None, wait=True, timeout=None, + force=False): """Delete a volume. :param name_or_id: Name or unique ID of the volume. :param wait: If true, waits for volume to be deleted. :param timeout: Seconds to wait for volume deletion. None is forever. + :param force: Force delete volume even if the volume is in deleting + or error_deleting state. :raises: OpenStackCloudTimeout if wait time exceeded. :raises: OpenStackCloudException on operation error. @@ -4785,8 +4788,13 @@ class OpenStackCloud( with _utils.shade_exceptions("Error in deleting volume"): try: - self._volume_client.delete( - 'volumes/{id}'.format(id=volume['id'])) + if force: + self._volume_client.post( + 'volumes/{id}/action'.format(id=volume['id']), + json={'os-force_delete': None}) + else: + self._volume_client.delete( + 'volumes/{id}'.format(id=volume['id'])) except OpenStackCloudURINotFound: self.log.debug( "Volume {id} not found when deleting. Ignoring.".format( diff --git a/shade/tests/unit/test_volume.py b/shade/tests/unit/test_volume.py index c4ff2aa10..bade4bd46 100644 --- a/shade/tests/unit/test_volume.py +++ b/shade/tests/unit/test_volume.py @@ -279,6 +279,27 @@ class TestVolume(base.RequestsMockTestCase): self.assertFalse(self.cloud.delete_volume(volume['id'])) self.assert_calls() + def test_delete_volume_force(self): + vol = {'id': 'volume001', 'status': 'attached', + 'name': '', 'attachments': []} + volume = meta.obj_to_munch(fakes.FakeVolume(**vol)) + self.register_uris([ + dict(method='GET', + uri=self.get_mock_url( + 'volumev2', 'public', append=['volumes', 'detail']), + json={'volumes': [volume]}), + dict(method='POST', + uri=self.get_mock_url( + 'volumev2', 'public', + append=['volumes', volume.id, 'action']), + json={'os-force_delete': None}), + dict(method='GET', + uri=self.get_mock_url( + 'volumev2', 'public', append=['volumes', 'detail']), + json={'volumes': []})]) + self.assertTrue(self.cloud.delete_volume(volume['id'], force=True)) + self.assert_calls() + def test_list_volumes_with_pagination(self): vol1 = meta.obj_to_munch(fakes.FakeVolume('01', 'available', 'vol1')) vol2 = meta.obj_to_munch(fakes.FakeVolume('02', 'available', 'vol2'))