summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorBrian Waldon <brian.waldon@rackspace.com>2011-07-26 16:13:09 -0400
committerBrian Waldon <brian.waldon@rackspace.com>2011-07-26 16:13:09 -0400
commit241a926ed682cb6154ff8f37c4940e7b5885b6fe (patch)
tree6fc57070e7ffc644930c3ef197fcc46ecf97b9f8 /nova/api
parentb45fa225f9477f4bae11cd379288db459d4b3c02 (diff)
moved v1.1 image creation from /images to /servers/<id>/action
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/images.py37
-rw-r--r--nova/api/openstack/servers.py93
2 files changed, 106 insertions, 24 deletions
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 30e4fd389..517a51662 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -98,6 +98,20 @@ class Controller(object):
self._image_service.delete(context, id)
return webob.exc.HTTPNoContent()
+ def get_builder(self, request):
+ """Indicates that you must use a Controller subclass."""
+ raise NotImplementedError()
+
+ def _server_id_from_req(self, req, data):
+ raise NotImplementedError()
+
+ def _get_extra_properties(self, req, data):
+ return {}
+
+
+class ControllerV10(Controller):
+ """Version 1.0 specific controller logic."""
+
def create(self, req, body):
"""Snapshot or backup a server instance and save the image.
@@ -158,20 +172,6 @@ class Controller(object):
return dict(image=self.get_builder(req).build(image, detail=True))
def get_builder(self, request):
- """Indicates that you must use a Controller subclass."""
- raise NotImplementedError()
-
- def _server_id_from_req(self, req, data):
- raise NotImplementedError()
-
- def _get_extra_properties(self, req, data):
- return {}
-
-
-class ControllerV10(Controller):
- """Version 1.0 specific controller logic."""
-
- def get_builder(self, request):
"""Property to get the ViewBuilder class we need to use."""
base_url = request.application_url
return images_view.ViewBuilderV10(base_url)
@@ -278,6 +278,9 @@ class ControllerV11(Controller):
server_ref)
return {'instance_ref': server_ref}
+ def create(self, *args, **kwargs):
+ raise webob.exc.HTTPMethodNotAllowed()
+
class ImageXMLSerializer(wsgi.XMLDictSerializer):
@@ -369,12 +372,6 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer):
image_dict['image'])
return self.to_xml_string(node, True)
- def create(self, image_dict):
- xml_doc = minidom.Document()
- node = self._image_to_xml_detailed(xml_doc,
- image_dict['image'])
- return self.to_xml_string(node, True)
-
def create_resource(version='1.0'):
controller = {
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index d7cabb067..5371e62c9 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -14,6 +14,7 @@
# under the License.
import base64
+import os
import traceback
from webob import exc
@@ -155,20 +156,26 @@ class Controller(object):
"""Multi-purpose method used to reboot, rebuild, or
resize a server"""
- actions = {
+ self.actions = {
'changePassword': self._action_change_password,
'reboot': self._action_reboot,
'resize': self._action_resize,
'confirmResize': self._action_confirm_resize,
'revertResize': self._action_revert_resize,
'rebuild': self._action_rebuild,
- 'migrate': self._action_migrate}
+ 'migrate': self._action_migrate,
+ 'createImage': self._action_create_image,
+ }
- for key in actions.keys():
+
+ for key in self.actions.keys():
if key in body:
- return actions[key](body, req, id)
+ return self.actions[key](body, req, id)
raise exc.HTTPNotImplemented()
+ def _action_create_image(self, input_dict, req, id):
+ return exc.HTTPNotImplemented()
+
def _action_change_password(self, input_dict, req, id):
return exc.HTTPNotImplemented()
@@ -585,6 +592,80 @@ class ControllerV11(Controller):
return webob.Response(status_int=202)
+ def _action_create_image(self, input_dict, req, instance_id):
+ """Snapshot or backup a server instance and save the image.
+
+ Images now have an `image_type` associated with them, which can be
+ 'snapshot' or the backup type, like 'daily' or 'weekly'.
+
+ If the image_type is backup-like, then the rotation factor can be
+ included and that will cause the oldest backups that exceed the
+ rotation factor to be deleted.
+
+ """
+ entity = input_dict.get('createImage', {})
+
+ def get_param(param):
+ try:
+ return entity[param]
+ except KeyError:
+ msg = _("Missing required param: %s") % param
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ context = req.environ['nova.context']
+
+ image_name = get_param("name")
+ image_type = entity.get("image_type", "snapshot")
+
+ # preserve link to server in image properties
+ server_ref = os.path.join(req.application_url,
+ 'servers',
+ str(instance_id))
+ props = {'instance_ref': server_ref}
+
+ metadata = entity.get('metadata', {})
+ try:
+ props.update(metadata)
+ except ValueError:
+ msg = _("Invalid metadata")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ if image_type == "snapshot":
+ image = self.compute_api.snapshot(context,
+ instance_id,
+ image_name,
+ extra_properties=props)
+
+ elif image_type == "backup":
+ # NOTE(sirp): Unlike snapshot, backup is not a customer facing
+ # API call; rather, it's used by the internal backup scheduler
+ if not FLAGS.allow_admin_api:
+ msg = _("Admin API Required")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ backup_type = get_param("backup_type")
+ rotation = int(get_param("rotation"))
+
+ image = self.compute_api.backup(context,
+ instance_id,
+ image_name,
+ backup_type,
+ rotation,
+ extra_properties=props)
+ else:
+ msg = _("Invalid image_type '%s'") % image_type
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+
+ # build location of newly-created image entity
+ image_ref = os.path.join(req.application_url,
+ 'images',
+ str(image['id']))
+
+ resp = webob.Response(status_int=202)
+ resp.headers['Location'] = image_ref
+ return resp
+
def get_default_xmlns(self, req):
return common.XML_NS_V11
@@ -593,6 +674,10 @@ class ControllerV11(Controller):
return self.helper._get_server_admin_password_new_style(server)
+
+
+
+
class HeadersSerializer(wsgi.ResponseHeadersSerializer):
def delete(self, response, data):