diff --git a/cinder/api/v2/volumes.py b/cinder/api/v2/volumes.py index 720b7cbe153..d56fe6b7c2a 100644 --- a/cinder/api/v2/volumes.py +++ b/cinder/api/v2/volumes.py @@ -175,6 +175,9 @@ def delete(self, req, id): self.volume_api.delete(context, volume) except exception.NotFound: raise exc.HTTPNotFound() + except exception.VolumeAttached: + explanation = 'Volume cannot be deleted while in attached state' + raise exc.HTTPBadRequest(explanation=explanation) return webob.Response(status_int=202) @wsgi.serializers(xml=VolumesTemplate) diff --git a/cinder/tests/api/v2/test_volumes.py b/cinder/tests/api/v2/test_volumes.py index 9b08ab376a2..7264c30a497 100644 --- a/cinder/tests/api/v2/test_volumes.py +++ b/cinder/tests/api/v2/test_volumes.py @@ -658,6 +658,16 @@ def test_volume_delete(self): resp = self.controller.delete(req, 1) self.assertEqual(resp.status_int, 202) + def test_volume_delete_attached(self): + def stub_volume_attached(self, context, volume, force=False): + raise exception.VolumeAttached(volume_id=volume['id']) + self.stubs.Set(volume_api.API, "delete", stub_volume_attached) + + req = fakes.HTTPRequest.blank('/v2/volumes/1') + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.delete, + req, 1) + def test_volume_delete_no_volume(self): self.stubs.Set(volume_api.API, "get", stubs.stub_volume_get_notfound) diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index f9ec0130268..39874c81d6a 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -561,6 +561,24 @@ def test_force_delete_volume(self): # clean up self.volume.delete_volume(self.context, volume['id']) + def test_cant_force_delete_attached_volume(self): + """Test volume can't be force delete in attached state""" + volume = self._create_volume() + self.volume.create_volume(self.context, volume['id']) + volume['status'] = 'in-use' + volume['attach_status'] = 'attached' + volume['host'] = 'fakehost' + + volume_api = cinder.volume.api.API() + + self.assertRaises(exception.VolumeAttached, + volume_api.delete, + self.context, + volume, + force=True) + + self.volume.delete_volume(self.context, volume['id']) + def test_cant_delete_volume_with_snapshots(self): """Test volume can't be deleted with dependent snapshots.""" volume = self._create_volume() diff --git a/cinder/volume/api.py b/cinder/volume/api.py index 68550b5e318..d24e8a0e766 100644 --- a/cinder/volume/api.py +++ b/cinder/volume/api.py @@ -320,6 +320,10 @@ def delete(self, context, volume, force=False): msg = _("Volume status must be available or error") raise exception.InvalidVolume(reason=msg) + if volume['attach_status'] == "attached": + # Volume is still attached, need to detach first + raise exception.VolumeAttached(volume_id=volume_id) + snapshots = self.db.snapshot_get_all_for_volume(context, volume_id) if len(snapshots): msg = _("Volume still has %d dependent snapshots") % len(snapshots)