summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/v2/contrib/admin_actions.py81
-rw-r--r--nova/api/openstack/v2/servers.py73
2 files changed, 79 insertions, 75 deletions
diff --git a/nova/api/openstack/v2/contrib/admin_actions.py b/nova/api/openstack/v2/contrib/admin_actions.py
index 46a4ec6f0..27bcf96c7 100644
--- a/nova/api/openstack/v2/contrib/admin_actions.py
+++ b/nova/api/openstack/v2/contrib/admin_actions.py
@@ -12,11 +12,13 @@
# License for the specific language governing permissions and limitations
# under the License.
+import os.path
import traceback
import webob
from webob import exc
+from nova.api.openstack import common
from nova.api.openstack.v2 import extensions
from nova import compute
from nova import exception
@@ -30,8 +32,10 @@ LOG = logging.getLogger("nova.api.openstack.v2.contrib.admin_actions")
class Admin_actions(extensions.ExtensionDescriptor):
- """Adds admin-only server actions: pause, unpause, suspend,
- resume, migrate, resetNetwork, injectNetworkInfo, lock and unlock
+ """Enable admin-only server actions
+
+ Actions include: pause, unpause, suspend, resume, migrate,
+ resetNetwork, injectNetworkInfo, lock, unlock, createBackup
"""
name = "AdminActions"
@@ -173,6 +177,75 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
+ def _create_backup(self, input_dict, req, instance_id):
+ """Backup a server instance.
+
+ 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.
+
+ """
+ context = req.environ["nova.context"]
+
+ try:
+ entity = input_dict["createBackup"]
+ except (KeyError, TypeError):
+ raise exc.HTTPBadRequest(_("Malformed request body"))
+
+ try:
+ image_name = entity["name"]
+ backup_type = entity["backup_type"]
+ rotation = entity["rotation"]
+
+ except KeyError as missing_key:
+ msg = _("createBackup entity requires %s attribute") % missing_key
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ except TypeError:
+ msg = _("Malformed createBackup entity")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ rotation = int(rotation)
+ except ValueError:
+ msg = _("createBackup attribute 'rotation' must be an integer")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ # preserve link to server in image properties
+ server_ref = os.path.join(req.application_url, 'servers', instance_id)
+ props = {'instance_ref': server_ref}
+
+ metadata = entity.get('metadata', {})
+ common.check_img_metadata_quota_limit(context, metadata)
+ try:
+ props.update(metadata)
+ except ValueError:
+ msg = _("Invalid metadata")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ instance = self.compute_api.get(context, instance_id)
+ except exception.NotFound:
+ raise exc.HTTPNotFound(_("Instance not found"))
+
+ image = self.compute_api.backup(context,
+ instance,
+ image_name,
+ backup_type,
+ rotation,
+ extra_properties=props)
+
+ # build location of newly-created image entity
+ image_id = str(image['id'])
+ image_ref = os.path.join(req.application_url, 'images', image_id)
+
+ resp = webob.Response(status_int=202)
+ resp.headers['Location'] = image_ref
+ return resp
+
def get_actions(self):
actions = [
#TODO(bcwaldon): These actions should be prefixed with 'os-'
@@ -183,6 +256,10 @@ class Admin_actions(extensions.ExtensionDescriptor):
extensions.ActionExtension("servers", "migrate", self._migrate),
extensions.ActionExtension("servers",
+ "createBackup",
+ self._create_backup),
+
+ extensions.ActionExtension("servers",
"resetNetwork",
self._reset_network),
diff --git a/nova/api/openstack/v2/servers.py b/nova/api/openstack/v2/servers.py
index cb0ece1f0..57ae5306f 100644
--- a/nova/api/openstack/v2/servers.py
+++ b/nova/api/openstack/v2/servers.py
@@ -500,12 +500,6 @@ class Controller(wsgi.Controller):
'createImage': self._action_create_image,
}
- if FLAGS.allow_admin_api:
- admin_actions = {
- 'createBackup': self._action_create_backup,
- }
- _actions.update(admin_actions)
-
for key in body:
if key in _actions:
return _actions[key](body, req, id)
@@ -516,68 +510,6 @@ class Controller(wsgi.Controller):
msg = _("Invalid request body")
raise exc.HTTPBadRequest(explanation=msg)
- def _action_create_backup(self, input_dict, req, instance_id):
- """Backup a server instance.
-
- 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.
-
- """
- context = req.environ["nova.context"]
- entity = input_dict["createBackup"]
-
- try:
- image_name = entity["name"]
- backup_type = entity["backup_type"]
- rotation = entity["rotation"]
-
- except KeyError as missing_key:
- msg = _("createBackup entity requires %s attribute") % missing_key
- raise exc.HTTPBadRequest(explanation=msg)
-
- except TypeError:
- msg = _("Malformed createBackup entity")
- raise exc.HTTPBadRequest(explanation=msg)
-
- try:
- rotation = int(rotation)
- except ValueError:
- msg = _("createBackup attribute 'rotation' must be an integer")
- raise exc.HTTPBadRequest(explanation=msg)
-
- # preserve link to server in image properties
- server_ref = os.path.join(req.application_url, 'servers', instance_id)
- props = {'instance_ref': server_ref}
-
- metadata = entity.get('metadata', {})
- common.check_img_metadata_quota_limit(context, metadata)
- try:
- props.update(metadata)
- except ValueError:
- msg = _("Invalid metadata")
- raise exc.HTTPBadRequest(explanation=msg)
-
- instance = self._get_server(context, instance_id)
-
- image = self.compute_api.backup(context,
- instance,
- image_name,
- backup_type,
- rotation,
- extra_properties=props)
-
- # build location of newly-created image entity
- image_id = str(image['id'])
- image_ref = os.path.join(req.application_url, 'images', image_id)
-
- resp = webob.Response(status_int=202)
- resp.headers['Location'] = image_ref
- return resp
-
def _action_confirm_resize(self, input_dict, req, id):
context = req.environ['nova.context']
instance = self._get_server(context, id)
@@ -1021,7 +953,6 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
action_deserializer = {
'createImage': self._action_create_image,
- 'createBackup': self._action_create_backup,
'changePassword': self._action_change_password,
'reboot': self._action_reboot,
'rebuild': self._action_rebuild,
@@ -1037,10 +968,6 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
def _action_create_image(self, node):
return self._deserialize_image_action(node, ('name',))
- def _action_create_backup(self, node):
- attributes = ('name', 'backup_type', 'rotation')
- return self._deserialize_image_action(node, attributes)
-
def _action_change_password(self, node):
if not node.hasAttribute("adminPass"):
raise AttributeError("No adminPass was specified in request")