Add os-assisted-volume-snapshots extension
Add a new API extension that exposes assisted volume snapshot capabilities. This extension is admin only by default. We expect it to only be called by Cinder. If you have your deployment set up in such a way that your adminURL is different from the public, this extension can only be loaded in the admin API instance. Cinder will pull that URL out of the service catalog to use. Part of blueprint qemu-assisted-snapshots Change-Id: I79e22ab6ef66fa16dc534a4336e766065702b2f5
This commit is contained in:
19 changed files with 315 additions and 13 deletions
@@ -128,6 +128,14 @@
"namespace": "http://docs.openstack.org/compute/ext/aggregates/api/v1.1",
"updated": "2012年01月12日T00:00:00+00:00"
},
{
"alias": "os-assisted-volume-snapshots",
"description": "Assisted volume snapshots.",
"links": [],
"name": "AssistedVolumeSnapshots",
"namespace": "http://docs.openstack.org/compute/ext/assisted-volume-snapshots/api/v2",
"updated": "2013年08月15日T00:00:00-00:00"
},
{
"alias": "os-attach-interfaces",
"description": "Attach interface support.",
@@ -472,14 +480,6 @@
"namespace": "http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1",
"updated": "2011年08月08日T00:00:00+00:00"
},
{
"alias": "os-user-quotas",
"description": "Project user quota support.",
"links": [],
"name": "UserQuotas",
"namespace": "http://docs.openstack.org/compute/ext/user_quotas/api/v1.1",
"updated": "2013年07月18日T00:00:00+00:00"
},
{
"alias": "os-rescue",
"description": "Instance rescue mode.",
@@ -584,6 +584,14 @@
"namespace": "http://docs.openstack.org/compute/ext/userdata/api/v1.1",
"updated": "2012年08月07日T00:00:00+00:00"
},
{
"alias": "os-user-quotas",
"description": "Project user quota support.",
"links": [],
"name": "UserQuotas",
"namespace": "http://docs.openstack.org/compute/ext/user_quotas/api/v1.1",
"updated": "2013年07月18日T00:00:00+00:00"
},
{
"alias": "os-virtual-interfaces",
"description": "Virtual interface support.",
@@ -609,4 +617,4 @@
"updated": "2011年03月25日T00:00:00+00:00"
}
]
}
}
@@ -52,6 +52,9 @@
<extension alias="os-aggregates" updated="2012年01月12日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/aggregates/api/v1.1" name="Aggregates">
<description>Admin-only aggregate administration.</description>
</extension>
<extension alias="os-assisted-volume-snapshots" updated="2013年08月15日T00:00:00-00:00" namespace="http://docs.openstack.org/compute/ext/assisted-volume-snapshots/api/v2" name="AssistedVolumeSnapshots">
<description>Assisted volume snapshots.</description>
</extension>
<extension alias="os-attach-interfaces" updated="2012年07月22日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/interfaces/api/v1.1" name="AttachInterfaces">
<description>Attach interface support.</description>
</extension>
@@ -197,9 +200,6 @@
<extension alias="os-quota-sets" updated="2011年08月08日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/quotas-sets/api/v1.1" name="Quotas">
<description>Quotas management support.</description>
</extension>
<extension alias="os-user-quotas" updated="2013年07月18日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/user_quotas/api/v1.1" name="UserQuotas">
<description>Project user quota support.</description>
</extension>
<extension alias="os-rescue" updated="2011年08月18日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/rescue/api/v1.1" name="Rescue">
<description>Instance rescue mode.</description>
</extension>
@@ -239,6 +239,9 @@
<extension alias="os-user-data" updated="2012年08月07日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/userdata/api/v1.1" name="UserData">
<description>Add user_data to the Create Server v1.1 API.</description>
</extension>
<extension alias="os-user-quotas" updated="2013年07月18日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/user_quotas/api/v1.1" name="UserQuotas">
<description>Project user quota support.</description>
</extension>
<extension alias="os-virtual-interfaces" updated="2011年08月17日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/virtual_interfaces/api/v1.1" name="VirtualInterfaces">
<description>Virtual interface support.</description>
</extension>
@@ -248,4 +251,4 @@
<extension alias="os-volumes" updated="2011年03月25日T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/volumes/api/v1.1" name="Volumes">
<description>Volumes support.</description>
</extension>
</extensions>
</extensions>
@@ -0,0 +1,10 @@
{
"snapshot": {
"display_name": "snap-001",
"display_description": "Daily backup",
"volume_id": "521752a6-acf6-4b2d-bc7a-119f9148cd8c",
"force": false,
"assisted": true,
"create_info": {}
}
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<snapshot>
<display_name>snap-001</display_name>
<display_description>Daily backup</display_description>
<volume_id>521752a6-acf6-4b2d-bc7a-119f9148cd8c</volume_id>
<force>false</force>
<assisted>true</assisted>
<create_info/>
</snapshot>
@@ -0,0 +1,6 @@
{
"snapshot": {
"id": 100,
"volumeId": "521752a6-acf6-4b2d-bc7a-119f9148cd8c"
}
}
@@ -0,0 +1,2 @@
<?xml version='1.0' encoding='UTF-8'?>
<snapshot volumeId="521752a6-acf6-4b2d-bc7a-119f9148cd8c" id="100"/>
@@ -243,6 +243,8 @@
"compute_extension:migrations:index": "rule:admin_api",
"compute_extension:v3:os-migrations:index": "rule:admin_api",
"compute_extension:v3:os-migrations:discoverable": "",
"compute_extension:os-assisted-volume-snapshots:create": "rule:admin_api",
"compute_extension:os-assisted-volume-snapshots:delete": "rule:admin_api",
"volume:create": "",
110
nova/api/openstack/compute/contrib/assisted_volume_snapshots.py
Normal file
110
nova/api/openstack/compute/contrib/assisted_volume_snapshots.py
Normal file
@@ -0,0 +1,110 @@
# 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 webob
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova import compute
from nova import exception
from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
LOG = logging.getLogger(__name__)
authorize = extensions.extension_authorizer('compute',
'os-assisted-volume-snapshots')
def make_snapshot(elem):
elem.set('id')
elem.set('volumeId')
class SnapshotTemplate(xmlutil.TemplateBuilder):
def construct(self):
root = xmlutil.TemplateElement('snapshot', selector='snapshot')
make_snapshot(root)
return xmlutil.MasterTemplate(root, 1)
class AssistedVolumeSnapshotsController(wsgi.Controller):
def __init__(self):
self.compute_api = compute.API()
super(AssistedVolumeSnapshotsController, self).__init__()
@wsgi.serializers(xml=SnapshotTemplate)
def create(self, req, body):
"""Creates a new snapshot."""
context = req.environ['nova.context']
authorize(context, action='create')
if not self.is_valid_body(body, 'snapshot'):
raise webob.exc.HTTPBadRequest()
try:
snapshot = body['snapshot']
create_info = snapshot['create_info']
volume_id = snapshot['volume_id']
except KeyError:
raise webob.exc.HTTPBadRequest()
LOG.audit(_("Create assisted snapshot from volume %s"), volume_id,
context=context)
return self.compute_api.volume_snapshot_create(context, volume_id,
create_info)
def delete(self, req, id):
"""Delete a snapshot."""
context = req.environ['nova.context']
authorize(context, action='delete')
LOG.audit(_("Delete snapshot with id: %s"), id, context=context)
delete_metadata = {}
delete_metadata.update(req.GET)
try:
delete_info = jsonutils.loads(delete_metadata['delete_info'])
volume_id = delete_info['volume_id']
except (KeyError, ValueError) as e:
raise webob.exc.HTTPBadRequest(explanation=str(e))
try:
self.compute_api.volume_snapshot_delete(context, volume_id,
id, delete_info)
except exception.NotFound:
return webob.exc.HTTPNotFound()
return webob.Response(status_int=204)
class Assisted_volume_snapshots(extensions.ExtensionDescriptor):
"""Assisted volume snapshots."""
name = "AssistedVolumeSnapshots"
alias = "os-assisted-volume-snapshots"
namespace = ("http://docs.openstack.org/compute/ext/"
"assisted-volume-snapshots/api/v2")
updated = "2013年08月29日T00:00:00-00:00"
def get_resources(self):
resource = extensions.ResourceExtension('os-assisted-volume-snapshots',
AssistedVolumeSnapshotsController())
return [resource]
@@ -1,4 +1,5 @@
# Copyright 2013 Josh Durgin
# Copyright 2013 Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -20,6 +21,8 @@ from oslo.config import cfg
import webob
from webob import exc
from nova.api.openstack.compute.contrib import assisted_volume_snapshots as \
assisted_snaps
from nova.api.openstack.compute.contrib import volumes
from nova.api.openstack import extensions
from nova.compute import api as compute_api
@@ -79,6 +82,16 @@ def fake_delete_snapshot(self, context, snapshot_id):
pass
def fake_compute_volume_snapshot_delete(self, context, volume_id, snapshot_id,
delete_info):
pass
def fake_compute_volume_snapshot_create(self, context, volume_id,
create_info):
pass
def fake_get_instance_bdms(self, context, instance):
return [{'id': 1,
'instance_uuid': instance['uuid'],
@@ -793,3 +806,51 @@ class DeleteSnapshotTestCase(test.TestCase):
self.req.method = 'DELETE'
result = self.controller.delete(self.req, result['snapshot']['id'])
self.assertEqual(result.status_int, 202)
class AssistedSnapshotCreateTestCase(test.TestCase):
def setUp(self):
super(AssistedSnapshotCreateTestCase, self).setUp()
self.controller = assisted_snaps.AssistedVolumeSnapshotsController()
self.stubs.Set(compute_api.API, 'volume_snapshot_create',
fake_compute_volume_snapshot_create)
def test_assisted_create(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-assisted-volume-snapshots')
body = {'snapshot': {'volume_id': 1, 'create_info': {}}}
req.method = 'POST'
self.controller.create(req, body=body)
def test_assisted_create_missing_create_info(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-assisted-volume-snapshots')
body = {'snapshot': {'volume_id': 1}}
req.method = 'POST'
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.create,
req, body=body)
class AssistedSnapshotDeleteTestCase(test.TestCase):
def setUp(self):
super(AssistedSnapshotDeleteTestCase, self).setUp()
self.controller = assisted_snaps.AssistedVolumeSnapshotsController()
self.stubs.Set(compute_api.API, 'volume_snapshot_delete',
fake_compute_volume_snapshot_delete)
def test_assisted_delete(self):
params = {
'delete_info': jsonutils.dumps({'volume_id': 1}),
}
req = fakes.HTTPRequest.blank(
'/v2/fake/os-assisted-volume-snapshots?%s' %
'&'.join(['%s=%s' % (k, v) for k, v in params.iteritems()]))
req.method = 'DELETE'
result = self.controller.delete(req, '5')
self.assertEqual(result.status_int, 204)
def test_assisted_delete_missing_delete_info(self):
req = fakes.HTTPRequest.blank('/v2/fake/os-assisted-volume-snapshots')
req.method = 'DELETE'
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.delete,
req, '5')
@@ -176,6 +176,7 @@ class ExtensionControllerTest(ExtensionTestCase):
self.ext_list = [
"AdminActions",
"Aggregates",
"AssistedVolumeSnapshots",
"AvailabilityZone",
"Agents",
"Certificates",
@@ -679,11 +679,20 @@ def stub_snapshot_create(self, context, volume_id, name, description):
display_description=description)
def stub_compute_volume_snapshot_create(self, context, volume_id, create_info):
return {'snapshot': {'id': 100, 'volumeId': volume_id}}
def stub_snapshot_delete(self, context, snapshot_id):
if snapshot_id == '-1':
raise exc.NotFound
def stub_compute_volume_snapshot_delete(self, context, volume_id, snapshot_id,
delete_info):
pass
def stub_snapshot_get(self, context, snapshot_id):
if snapshot_id == '-1':
raise exc.NotFound
@@ -279,6 +279,8 @@ policy_data = """
"compute_extension:v3:os-used-limits:tenant": "is_admin:True",
"compute_extension:migrations:index": "is_admin:True",
"compute_extension:v3:os-migrations:index": "is_admin:True",
"compute_extension:os-assisted-volume-snapshots:create": "",
"compute_extension:os-assisted-volume-snapshots:delete": "",
"volume:create": "",
"volume:get": "",
@@ -136,6 +136,14 @@
"namespace": "http://docs.openstack.org/compute/ext/agents/api/v2",
"updated": "%(timestamp)s"
},
{
"alias": "os-assisted-volume-snapshots",
"description": "%(text)s",
"links": [],
"name": "AssistedVolumeSnapshots",
"namespace": "http://docs.openstack.org/compute/ext/assisted-volume-snapshots/api/v2",
"updated": "%(timestamp)s"
},
{
"alias": "os-attach-interfaces",
"description": "Attach interface support.",
@@ -228,4 +228,7 @@
<extension alias="os-migrations" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/migrations/api/v2.0" name="Migrations">
<description>%(text)s</description>
</extension>
<extension alias="os-assisted-volume-snapshots" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/assisted-volume-snapshots/api/v2" name="AssistedVolumeSnapshots">
<description>%(text)s</description>
</extension>
</extensions>
@@ -0,0 +1,10 @@
{
"snapshot": {
"display_name": "%(snapshot_name)s",
"display_description": "%(description)s",
"volume_id": "%(volume_id)s",
"force": false,
"assisted": true,
"create_info": {}
}
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<snapshot>
<display_name>%(snapshot_name)s</display_name>
<display_description>%(description)s</display_description>
<volume_id>%(volume_id)s</volume_id>
<force>false</force>
<assisted>true</assisted>
<create_info/>
</snapshot>
@@ -0,0 +1,6 @@
{
"snapshot": {
"id": 100,
"volumeId": "%(uuid)s"
}
}
@@ -0,0 +1,2 @@
<?xml version='1.0' encoding='UTF-8'?>
<snapshot volumeId="521752a6-acf6-4b2d-bc7a-119f9148cd8c" id="100"/>
@@ -3697,6 +3697,47 @@ class SnapshotsSampleXmlTests(SnapshotsSampleJsonTests):
ctype = "xml"
class AssistedVolumeSnapshotsJsonTest(ApiSampleTestBaseV2):
"""Assisted volume snapshots."""
extension_name = ("nova.api.openstack.compute.contrib."
"assisted_volume_snapshots.Assisted_volume_snapshots")
def _create_assisted_snapshot(self, subs):
self.stubs.Set(compute_api.API, 'volume_snapshot_create',
fakes.stub_compute_volume_snapshot_create)
response = self._do_post("os-assisted-volume-snapshots",
"snapshot-create-assisted-req",
subs)
return response
def test_snapshots_create_assisted(self):
subs = {
'snapshot_name': 'snap-001',
'description': 'Daily backup',
'volume_id': '521752a6-acf6-4b2d-bc7a-119f9148cd8c'
}
subs.update(self._get_regexes())
response = self._create_assisted_snapshot(subs)
self._verify_response("snapshot-create-assisted-resp",
subs, response, 200)
def test_snapshots_delete_assisted(self):
self.stubs.Set(compute_api.API, 'volume_snapshot_delete',
fakes.stub_compute_volume_snapshot_delete)
snapshot_id = '100'
response = self._do_delete(
'os-assisted-volume-snapshots/%s?delete_info='
'{"volume_id":"521752a6-acf6-4b2d-bc7a-119f9148cd8c"}'
% snapshot_id)
self.assertEqual(response.status, 204)
self.assertEqual(response.read(), '')
class AssistedVolumeSnapshotsXmlTest(AssistedVolumeSnapshotsJsonTest):
ctype = "xml"
class VolumeAttachmentsSampleBase(ServersSampleBase):
def _stub_compute_api_get_instance_bdms(self, server_id):
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.