]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
New update_snapshot_status API
authorEric Harney <eharney@redhat.com>
Fri, 19 Jul 2013 14:02:51 +0000 (10:02 -0400)
committerEric Harney <eharney@redhat.com>
Tue, 3 Sep 2013 12:37:43 +0000 (08:37 -0400)
Adds new snapshot_actions module

Update_snapshot_status: Allows updating of 'state' and
'progress' fields of a snapshot.  This is used by Nova
to inform Cinder of its outcome when performing snapshot
operations for attached volumes.  Updates are restricted
to a subset of possible start and finish states.

Implements blueprint qemu-assisted-snapshots

Change-Id: I54772f794b97e1cc6b24b121b757219248e37109

cinder/api/contrib/snapshot_actions.py [new file with mode: 0644]
cinder/tests/api/contrib/test_snapshot_actions.py [new file with mode: 0644]
cinder/tests/policy.json
etc/cinder/policy.json

diff --git a/cinder/api/contrib/snapshot_actions.py b/cinder/api/contrib/snapshot_actions.py
new file mode 100644 (file)
index 0000000..781f7a8
--- /dev/null
@@ -0,0 +1,107 @@
+#   Copyright 2013, Red Hat, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+
+from oslo.config import cfg
+import webob
+
+from cinder.api import extensions
+from cinder.api.openstack import wsgi
+from cinder import db
+from cinder.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+def authorize(context, action_name):
+    action = 'snapshot_actions:%s' % action_name
+    extensions.extension_authorizer('snapshot', action)(context)
+
+
+class SnapshotActionsController(wsgi.Controller):
+    def __init__(self, *args, **kwargs):
+        super(SnapshotActionsController, self).__init__(*args, **kwargs)
+        LOG.debug("SnapshotActionsController initialized")
+
+    @wsgi.action('os-update_snapshot_status')
+    def _update_snapshot_status(self, req, id, body):
+        """Update database fields related to status of a snapshot.
+
+           Intended for creation of snapshots, so snapshot state
+           must start as 'creating' and be changed to 'available',
+           'creating', or 'error'.
+        """
+
+        context = req.environ['cinder.context']
+        authorize(context, 'update_snapshot_status')
+
+        LOG.debug("body: %s" % body)
+        status = body['os-update_snapshot_status']['status']
+
+        # Allowed state transitions
+        status_map = {'creating': ['creating', 'available', 'error'],
+                      'deleting': ['deleting', 'error_deleting']}
+
+        current_snapshot = db.snapshot_get(context, id)
+
+        if current_snapshot['status'] not in status_map:
+            msg = _("Snapshot status %(cur)s not allowed for "
+                    "update_snapshot_status") % {
+                        'cur': current_snapshot['status']}
+            raise webob.exc.HTTPBadRequest(explanation=msg)
+
+        if status not in status_map[current_snapshot['status']]:
+            msg = _("Provided snapshot status %(provided)s not allowed for "
+                    "snapshot with status %(current)s.") % \
+                {'provided': status,
+                 'current': current_snapshot['status']}
+            raise webob.exc.HTTPBadRequest(explanation=msg)
+
+        update_dict = {'id': id,
+                       'status': status}
+
+        progress = body['os-update_snapshot_status'].get('progress', None)
+        if progress:
+            # This is expected to be a string like '73%'
+            msg = _('progress must be an integer percentage')
+            try:
+                integer = int(progress[:-1])
+            except ValueError:
+                raise webob.exc.HTTPBadRequest(explanation=msg)
+            if integer < 0 or integer > 100 or progress[-1] != '%':
+                raise webob.exc.HTTPBadRequest(explanation=msg)
+
+            update_dict.update({'progress': progress})
+
+        LOG.info("Updating snapshot %(id)s with info %(dict)s" %
+                 {'id': id, 'dict': update_dict})
+
+        db.snapshot_update(context, id, update_dict)
+        return webob.Response(status_int=202)
+
+
+class Snapshot_actions(extensions.ExtensionDescriptor):
+    """Enable snapshot manager actions."""
+
+    name = "SnapshotActions"
+    alias = "os-snapshot-actions"
+    namespace = \
+        "http://docs.openstack.org/volume/ext/snapshot-actions/api/v1.1"
+    updated = "2013-07-16T00:00:00+00:00"
+
+    def get_controller_extensions(self):
+        controller = SnapshotActionsController()
+        extension = extensions.ControllerExtension(self,
+                                                   'snapshots',
+                                                   controller)
+        return [extension]
diff --git a/cinder/tests/api/contrib/test_snapshot_actions.py b/cinder/tests/api/contrib/test_snapshot_actions.py
new file mode 100644 (file)
index 0000000..5ec2fed
--- /dev/null
@@ -0,0 +1,75 @@
+#   Copyright 2013, Red Hat, Inc.
+#
+#   Licensed under the Apache License, Version 2.0 (the "License"); you may
+#   not use this file except in compliance with the License. You may obtain
+#   a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#   License for the specific language governing permissions and limitations
+#   under the License.
+
+import datetime
+import uuid
+import webob
+
+from cinder import db
+from cinder import exception
+from cinder.openstack.common import jsonutils
+from cinder.openstack.common.rpc import common as rpc_common
+from cinder import test
+from cinder.tests.api import fakes
+from cinder.tests.api.v2 import stubs
+from cinder import volume
+from cinder.volume import api as volume_api
+
+
+class SnapshotActionsTest(test.TestCase):
+
+    def setUp(self):
+        super(SnapshotActionsTest, self).setUp()
+
+    def test_update_snapshot_status(self):
+        self.stubs.Set(db, 'snapshot_get', stub_snapshot_get)
+        self.stubs.Set(db, 'snapshot_update', stub_snapshot_update)
+
+        body = {'os-update_snapshot_status': {'status': 'available'}}
+        req = webob.Request.blank('/v2/fake/snapshots/1/action')
+        req.method = "POST"
+        req.body = jsonutils.dumps(body)
+        req.headers["content-type"] = "application/json"
+
+        res = req.get_response(fakes.wsgi_app())
+        self.assertEqual(res.status_int, 202)
+
+    def test_update_snapshot_status_invalid_status(self):
+        self.stubs.Set(db, 'snapshot_get', stub_snapshot_get)
+        body = {'os-update_snapshot_status': {'status': 'in-use'}}
+        req = webob.Request.blank('/v2/fake/snapshots/1/action')
+        req.method = "POST"
+        req.body = jsonutils.dumps(body)
+        req.headers["content-type"] = "application/json"
+
+        res = req.get_response(fakes.wsgi_app())
+        self.assertEqual(res.status_int, 400)
+
+
+def stub_snapshot_get(context, snapshot_id):
+    snapshot = stubs.stub_snapshot(snapshot_id)
+    if snapshot_id == 3:
+        snapshot['status'] = 'error'
+    elif snapshot_id == 1:
+        snapshot['status'] = 'creating'
+    elif snapshot_id == 7:
+        snapshot['status'] = 'available'
+    else:
+        snapshot['status'] = 'creating'
+
+    return snapshot
+
+
+def stub_snapshot_update(self, context, id, **kwargs):
+    pass
index 669f416dd82ef6dd1698ff43ab77cbc415f1be58..c8a8a9347b89570044e1db6579c5c56c59819bdd 100644 (file)
@@ -56,6 +56,8 @@
     "volume_extension:quotas:show": [],
     "volume_extension:quotas:update": [],
 
+    "snapshot_extension:snapshot_actions:update_snapshot_status": [],
+
     "volume:create_transfer": [],
     "volume:accept_transfer": [],
     "volume:delete_transfer": [],
index 7b3065c0a4c6920ade326f0c20481a12095f6168..a7fdab4122430c43431ecbc26d8f93ca52c98190 100644 (file)
@@ -50,5 +50,7 @@
     "backup:delete": [],
     "backup:get": [],
     "backup:get_all": [],
-    "backup:restore": []
+    "backup:restore": [],
+
+    "snapshot_extension:snapshot_actions:update_snapshot_status": []
 }