1
# Copyright 2012 OpenStack, LLC.
3
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
# not use this file except in compliance with the License. You may obtain
5
# a copy of the License at
7
# http://www.apache.org/licenses/LICENSE-2.0
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
# License for the specific language governing permissions and limitations
16
from xml.dom import minidom
18
from nova.api.openstack import extensions
19
from nova.api.openstack import wsgi
20
from nova.api.openstack import xmlutil
21
from nova import exception
22
from nova import flags
23
from nova.openstack.common import log as logging
24
from nova.openstack.common.rpc import common as rpc_common
25
from nova import volume
29
LOG = logging.getLogger(__name__)
32
def authorize(context, action_name):
33
action = 'volume_actions:%s' % action_name
34
extensions.extension_authorizer('volume', action)(context)
37
class VolumeToImageSerializer(xmlutil.TemplateBuilder):
39
root = xmlutil.TemplateElement('os-volume_upload_image',
40
selector='os-volume_upload_image')
42
root.set('updated_at')
44
root.set('display_description')
46
root.set('volume_type')
48
root.set('container_format')
49
root.set('disk_format')
50
root.set('image_name')
51
return xmlutil.MasterTemplate(root, 1)
54
class VolumeToImageDeserializer(wsgi.XMLDeserializer):
55
"""Deserializer to handle xml-formatted requests"""
56
def default(self, string):
57
dom = minidom.parseString(string)
58
action_node = dom.childNodes[0]
59
action_name = action_node.tagName
62
attributes = ["force", "image_name", "container_format", "disk_format"]
63
for attr in attributes:
64
if action_node.hasAttribute(attr):
65
action_data[attr] = action_node.getAttribute(attr)
66
if 'force' in action_data and action_data['force'] == 'True':
67
action_data['force'] = True
68
return {'body': {action_name: action_data}}
71
class VolumeActionsController(wsgi.Controller):
72
def __init__(self, *args, **kwargs):
73
super(VolumeActionsController, self).__init__(*args, **kwargs)
74
self.volume_api = volume.API()
77
@wsgi.action('os-volume_upload_image')
78
@wsgi.serializers(xml=VolumeToImageSerializer)
79
@wsgi.deserializers(xml=VolumeToImageDeserializer)
80
def _volume_upload_image(self, req, id, body):
81
"""Uploads the specified volume to image service."""
82
context = req.environ['nova.context']
84
params = body['os-volume_upload_image']
85
except (TypeError, KeyError):
86
msg = _("Invalid request body")
87
raise webob.exc.HTTPBadRequest(explanation=msg)
89
if not params.get("image_name"):
90
msg = _("No image_name was specified in request.")
91
raise webob.exc.HTTPBadRequest(explanation=msg)
93
force = params.get('force', False)
95
volume = self.volume_api.get(context, id)
96
except exception.VolumeNotFound, error:
97
raise webob.exc.HTTPNotFound(explanation=unicode(error))
98
authorize(context, "upload_image")
99
image_metadata = {"container_format": params.get("container_format",
101
"disk_format": params.get("disk_format", "raw"),
102
"name": params["image_name"]}
104
response = self.volume_api.copy_volume_to_image(context,
108
except exception.InvalidVolume, error:
109
raise webob.exc.HTTPBadRequest(explanation=unicode(error))
110
except ValueError, error:
111
raise webob.exc.HTTPBadRequest(explanation=unicode(error))
112
except rpc_common.RemoteError as error:
113
msg = "%(err_type)s: %(err_msg)s" % {'err_type': error.exc_type,
114
'err_msg': error.value}
115
raise webob.exc.HTTPBadRequest(explanation=msg)
116
return {'os-volume_upload_image': response}
119
class Volume_actions(extensions.ExtensionDescriptor):
120
"""Enable volume actions
123
name = "VolumeActions"
124
alias = "os-volume-actions"
125
namespace = "http://docs.openstack.org/volume/ext/volume-actions/api/v1.1"
126
updated = "2012-05-31T00:00:00+00:00"
128
def get_controller_extensions(self):
129
controller = VolumeActionsController()
130
extension = extensions.ControllerExtension(self, 'volumes', controller)