Add admin actions extension

The optional os-admin-actions extension adds new wsgi_actions to the
volumes/action resource and a new snapshots/action endpoint.

With this extension both controllers will support an os-reset_status
action to force a database update of a volume or snapshot that is stuck
in a failed/incorrect status. The os-reset_status action works
similarly to the compute api's os-reset_state action for instances.

The os-force_delete action behaves similarly to the "cinder-manage
volume delete" command and allows operators/admins to retry the delete
operation after it has gone into an error_deleting status with an admin
api call.

The os-admin-actions extension is enabled by default, but limited to the
admin api by the default policy.json rules. Individual admin actions
can be disabled with policy rules as well.

Example of os-reset_status action on a volume:

curl http://localhost:8776/v1/${PROJECT_ID}/volumes/${VOLUME_ID}/action \
    -H "x-auth-token: ${ADMIN_AUTH_TOKEN}" \
    -H 'content-type: application/json' \
    -d '{"os-reset_status": {"status": "error"}}'

The new admin only api can assist deployers who encounter bugs or
operational issues that result in failed actions.

It can also be used by future storage backends to support async callback
style status updates from long running actions or operations which have
encountered an error will be retried.

Also updates the api.openstack.wsgi.ControllerMetaclass to support
sub-classing wsgi.Controllers that define wsgi_actions.

Partial fix for bug #1039706

Change-Id: If795599d5150dea362279d75a75276f3166d0149
This commit is contained in:
Clay Gerrard
2012-09-14 16:15:35 +00:00
parent ebdc561553
commit c6584bb5c5
2 changed files with 50 additions and 0 deletions

View File

@@ -1,4 +1,6 @@
{ {
"admin_api": [["role:admin"]],
"context_is_admin": [["role:admin"], ["role:administrator"]], "context_is_admin": [["role:admin"], ["role:administrator"]],
"compute:create": [], "compute:create": [],
"compute:create:attach_network": [], "compute:create:attach_network": [],
@@ -149,6 +151,9 @@
"volume:get_all_snapshots": [], "volume:get_all_snapshots": [],
"volume_extension:volume_admin_actions:reset_status": [["rule:admin_api"]],
"volume_extension:snapshot_admin_actions:reset_status": [["rule:admin_api"]],
"volume_extension:volume_admin_actions:force_delete": [["rule:admin_api"]],
"volume_extension:types_manage": [], "volume_extension:types_manage": [],
"volume_extension:types_extra_specs": [], "volume_extension:types_extra_specs": [],

View File

@@ -322,6 +322,51 @@ class VolumeTestCase(test.TestCase):
snapshot_id) snapshot_id)
self.volume.delete_volume(self.context, volume['id']) self.volume.delete_volume(self.context, volume['id'])
def test_cant_delete_volume_in_use(self):
"""Test volume can't be deleted in invalid stats."""
# create a volume and assign to host
volume = self._create_volume()
self.volume.create_volume(self.context, volume['id'])
volume['status'] = 'in-use'
volume['host'] = 'fakehost'
volume_api = nova.volume.api.API()
# 'in-use' status raises InvalidVolume
self.assertRaises(exception.InvalidVolume,
volume_api.delete,
self.context,
volume)
# clean up
self.volume.delete_volume(self.context, volume['id'])
def test_force_delete_volume(self):
"""Test volume can be forced to delete."""
# create a volume and assign to host
volume = self._create_volume()
self.volume.create_volume(self.context, volume['id'])
volume['status'] = 'error_deleting'
volume['host'] = 'fakehost'
volume_api = nova.volume.api.API()
# 'error_deleting' volumes can't be deleted
self.assertRaises(exception.InvalidVolume,
volume_api.delete,
self.context,
volume)
# delete with force
volume_api.delete(self.context, volume, force=True)
# status is deleting
volume = db.volume_get(context.get_admin_context(), volume['id'])
self.assertEquals(volume['status'], 'deleting')
# clean up
self.volume.delete_volume(self.context, volume['id'])
def test_cant_delete_volume_with_snapshots(self): def test_cant_delete_volume_with_snapshots(self):
"""Test snapshot can be created and deleted.""" """Test snapshot can be created and deleted."""
volume = self._create_volume() volume = self._create_volume()