diff options
| author | Brian Waldon <brian.waldon@rackspace.com> | 2011-07-26 16:13:09 -0400 |
|---|---|---|
| committer | Brian Waldon <brian.waldon@rackspace.com> | 2011-07-26 16:13:09 -0400 |
| commit | 241a926ed682cb6154ff8f37c4940e7b5885b6fe (patch) | |
| tree | 6fc57070e7ffc644930c3ef197fcc46ecf97b9f8 /nova/api | |
| parent | b45fa225f9477f4bae11cd379288db459d4b3c02 (diff) | |
moved v1.1 image creation from /images to /servers/<id>/action
Diffstat (limited to 'nova/api')
| -rw-r--r-- | nova/api/openstack/images.py | 37 | ||||
| -rw-r--r-- | nova/api/openstack/servers.py | 93 |
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): |
