Merge "Add image metadata API extension"
This commit is contained in:
106
cinder/api/contrib/volume_image_metadata.py
Normal file
106
cinder/api/contrib/volume_image_metadata.py
Normal file
@@ -0,0 +1,106 @@
|
||||
# Copyright 2012 OpenStack, LLC.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""The Volume Image Metadata API extension."""
|
||||
|
||||
from cinder.api import extensions
|
||||
from cinder.api.openstack import wsgi
|
||||
from cinder.api import xmlutil
|
||||
from cinder import volume
|
||||
|
||||
|
||||
authorize = extensions.soft_extension_authorizer('volume',
|
||||
'volume_image_metadata')
|
||||
|
||||
|
||||
class VolumeImageMetadataController(wsgi.Controller):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(VolumeImageMetadataController, self).__init__(*args, **kwargs)
|
||||
self.volume_api = volume.API()
|
||||
|
||||
def _add_image_metadata(self, context, resp_volume):
|
||||
try:
|
||||
image_meta = self.volume_api.get_volume_image_metadata(
|
||||
context, resp_volume)
|
||||
except Exception:
|
||||
return
|
||||
else:
|
||||
if image_meta:
|
||||
resp_volume['volume_image_metadata'] = dict(
|
||||
image_meta.iteritems())
|
||||
|
||||
@wsgi.extends
|
||||
def show(self, req, resp_obj, id):
|
||||
context = req.environ['cinder.context']
|
||||
if authorize(context):
|
||||
resp_obj.attach(xml=VolumeImageMetadataTemplate())
|
||||
self._add_image_metadata(context, resp_obj.obj['volume'])
|
||||
|
||||
@wsgi.extends
|
||||
def detail(self, req, resp_obj):
|
||||
context = req.environ['cinder.context']
|
||||
if authorize(context):
|
||||
resp_obj.attach(xml=VolumesImageMetadataTemplate())
|
||||
for volume in list(resp_obj.obj.get('volumes', [])):
|
||||
self._add_image_metadata(context, volume)
|
||||
|
||||
|
||||
class Volume_image_metadata(extensions.ExtensionDescriptor):
|
||||
"""Show image metadata associated with the volume"""
|
||||
|
||||
name = "VolumeImageMetadata"
|
||||
alias = "os-vol-image-meta"
|
||||
namespace = ("http://docs.openstack.org/volume/ext/"
|
||||
"volume_image_metadata/api/v1")
|
||||
updated = "2012-12-07T00:00:00+00:00"
|
||||
|
||||
def get_controller_extensions(self):
|
||||
controller = VolumeImageMetadataController()
|
||||
extension = extensions.ControllerExtension(self, 'volumes', controller)
|
||||
return [extension]
|
||||
|
||||
|
||||
class VolumeImageMetadataMetadataTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('volume_image_metadata',
|
||||
selector='volume_image_metadata')
|
||||
elem = xmlutil.SubTemplateElement(root, 'meta',
|
||||
selector=xmlutil.get_items)
|
||||
elem.set('key', 0)
|
||||
elem.text = 1
|
||||
|
||||
return xmlutil.MasterTemplate(root, 1)
|
||||
|
||||
|
||||
class VolumeImageMetadataTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('volume', selector='volume')
|
||||
root.append(VolumeImageMetadataMetadataTemplate())
|
||||
|
||||
alias = Volume_image_metadata.alias
|
||||
namespace = Volume_image_metadata.namespace
|
||||
|
||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
||||
|
||||
|
||||
class VolumesImageMetadataTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('volumes')
|
||||
elem = xmlutil.SubTemplateElement(root, 'volume', selector='volume')
|
||||
elem.append(VolumeImageMetadataMetadataTemplate())
|
||||
|
||||
alias = Volume_image_metadata.alias
|
||||
namespace = Volume_image_metadata.namespace
|
||||
|
||||
return xmlutil.SlaveTemplate(root, 1, nsmap={alias: namespace})
|
||||
130
cinder/tests/api/contrib/test_volume_image_metadata.py
Normal file
130
cinder/tests/api/contrib/test_volume_image_metadata.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
#
|
||||
# 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 json
|
||||
import uuid
|
||||
from xml.dom import minidom
|
||||
|
||||
import webob
|
||||
|
||||
from cinder.api import common
|
||||
from cinder.api.openstack.wsgi import MetadataXMLDeserializer
|
||||
from cinder.api.openstack.wsgi import XMLDeserializer
|
||||
from cinder import test
|
||||
from cinder.tests.api import fakes
|
||||
from cinder import volume
|
||||
|
||||
|
||||
def fake_volume_get(*args, **kwargs):
|
||||
return {
|
||||
'id': 'fake',
|
||||
'host': 'host001',
|
||||
'status': 'available',
|
||||
'size': 5,
|
||||
'availability_zone': 'somewhere',
|
||||
'created_at': datetime.datetime.now(),
|
||||
'attach_status': None,
|
||||
'display_name': 'anothervolume',
|
||||
'display_description': 'Just another volume!',
|
||||
'volume_type_id': None,
|
||||
'snapshot_id': None,
|
||||
'project_id': 'fake',
|
||||
}
|
||||
|
||||
|
||||
def fake_volume_get_all(*args, **kwargs):
|
||||
return [fake_volume_get()]
|
||||
|
||||
|
||||
fake_image_metadata = {
|
||||
'image_id': 'someid',
|
||||
'image_name': 'fake',
|
||||
'kernel_id': 'somekernel',
|
||||
'ramdisk_id': 'someramdisk',
|
||||
}
|
||||
|
||||
|
||||
def fake_get_volume_image_metadata(*args, **kwargs):
|
||||
return fake_image_metadata
|
||||
|
||||
|
||||
class VolumeImageMetadataTest(test.TestCase):
|
||||
content_type = 'application/json'
|
||||
|
||||
def setUp(self):
|
||||
super(VolumeImageMetadataTest, self).setUp()
|
||||
self.stubs.Set(volume.API, 'get', fake_volume_get)
|
||||
self.stubs.Set(volume.API, 'get_all', fake_volume_get_all)
|
||||
self.stubs.Set(volume.API, 'get_volume_image_metadata',
|
||||
fake_get_volume_image_metadata)
|
||||
self.UUID = uuid.uuid4()
|
||||
|
||||
def _make_request(self, url):
|
||||
req = webob.Request.blank(url)
|
||||
req.accept = self.content_type
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
return res
|
||||
|
||||
def _get_image_metadata(self, body):
|
||||
return json.loads(body)['volume']['volume_image_metadata']
|
||||
|
||||
def _get_image_metadata_list(self, body):
|
||||
return [
|
||||
volume['volume_image_metadata']
|
||||
for volume in json.loads(body)['volumes']
|
||||
]
|
||||
|
||||
def test_get_volume(self):
|
||||
res = self._make_request('/v2/fake/volumes/%s' % self.UUID)
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(self._get_image_metadata(res.body),
|
||||
fake_image_metadata)
|
||||
|
||||
def test_list_detail_volumes(self):
|
||||
res = self._make_request('/v2/fake/volumes/detail')
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(self._get_image_metadata_list(res.body)[0],
|
||||
fake_image_metadata)
|
||||
|
||||
|
||||
class ImageMetadataXMLDeserializer(common.MetadataXMLDeserializer):
|
||||
metadata_node_name = "volume_image_metadata"
|
||||
|
||||
|
||||
class VolumeImageMetadataXMLTest(VolumeImageMetadataTest):
|
||||
content_type = 'application/xml'
|
||||
|
||||
def _get_image_metadata(self, body):
|
||||
deserializer = XMLDeserializer()
|
||||
volume = deserializer.find_first_child_named(
|
||||
minidom.parseString(body), 'volume')
|
||||
image_metadata = deserializer.find_first_child_named(
|
||||
volume, 'volume_image_metadata')
|
||||
return MetadataXMLDeserializer().extract_metadata(image_metadata)
|
||||
|
||||
def _get_image_metadata_list(self, body):
|
||||
deserializer = XMLDeserializer()
|
||||
volumes = deserializer.find_first_child_named(
|
||||
minidom.parseString(body), 'volumes')
|
||||
volume_list = deserializer.find_children_named(volumes, 'volume')
|
||||
image_metadata_list = [
|
||||
deserializer.find_first_child_named(
|
||||
volume, 'volume_image_metadata'
|
||||
)
|
||||
for volume in volume_list]
|
||||
return map(MetadataXMLDeserializer().extract_metadata,
|
||||
image_metadata_list)
|
||||
@@ -35,6 +35,7 @@
|
||||
"volume_extension:types_manage": [],
|
||||
"volume_extension:types_extra_specs": [],
|
||||
"volume_extension:extended_snapshot_attributes": [],
|
||||
"volume_extension:volume_image_metadata": [],
|
||||
"volume_extension:volume_host_attribute": [["rule:admin_api"]],
|
||||
"volume_extension:volume_tenant_attribute": [["rule:admin_api"]],
|
||||
"volume_extension:hosts": [["rule:admin_api"]]
|
||||
|
||||
@@ -489,6 +489,13 @@ class API(base.Base):
|
||||
return i['value']
|
||||
return None
|
||||
|
||||
@wrap_check_policy
|
||||
def get_volume_image_metadata(self, context, volume):
|
||||
db_data = self.db.volume_glance_metadata_get(context, volume['id'])
|
||||
return dict(
|
||||
(meta_entry.key, meta_entry.value) for meta_entry in db_data
|
||||
)
|
||||
|
||||
def _check_volume_availability(self, context, volume, force):
|
||||
"""Check if the volume can be used."""
|
||||
if volume['status'] not in ['available', 'in-use']:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"volume_extension:types_manage": [["rule:admin_api"]],
|
||||
"volume_extension:types_extra_specs": [["rule:admin_api"]],
|
||||
"volume_extension:extended_snapshot_attributes": [],
|
||||
"volume_extension:volume_image_metadata": [],
|
||||
|
||||
"volume_extension:quotas:show": [],
|
||||
"volume_extension:quotas:update_for_project": [["rule:admin_api"]],
|
||||
|
||||
Reference in New Issue
Block a user