~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to nova/api/openstack/compute/contrib/admin_actions.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-01-20 11:54:15 UTC
  • mto: This revision was merged to the branch mainline in revision 62.
  • Revision ID: package-import@ubuntu.com-20120120115415-h2ujma9o536o1ut6
Tags: upstream-2012.1~e3~20120120.12170
ImportĀ upstreamĀ versionĀ 2012.1~e3~20120120.12170

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#   Copyright 2011 Openstack, LLC.
 
2
#
 
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
 
6
#
 
7
#       http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
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
 
13
#   under the License.
 
14
 
 
15
import os.path
 
16
import traceback
 
17
 
 
18
import webob
 
19
from webob import exc
 
20
 
 
21
from nova.api.openstack import common
 
22
from nova.api.openstack import extensions
 
23
from nova.api.openstack import wsgi
 
24
from nova import compute
 
25
from nova import exception
 
26
from nova import flags
 
27
from nova import log as logging
 
28
from nova.scheduler import api as scheduler_api
 
29
 
 
30
 
 
31
FLAGS = flags.FLAGS
 
32
LOG = logging.getLogger("nova.api.openstack.compute.contrib.admin_actions")
 
33
 
 
34
 
 
35
class AdminActionsController(wsgi.Controller):
 
36
    def __init__(self, *args, **kwargs):
 
37
        super(AdminActionsController, self).__init__(*args, **kwargs)
 
38
        self.compute_api = compute.API()
 
39
 
 
40
    # TODO(bcwaldon): These action names should be prefixed with 'os-'
 
41
 
 
42
    @wsgi.action('pause')
 
43
    @exception.novaclient_converter
 
44
    @scheduler_api.redirect_handler
 
45
    def _pause(self, req, id, body):
 
46
        """Permit Admins to pause the server"""
 
47
        ctxt = req.environ['nova.context']
 
48
        try:
 
49
            server = self.compute_api.get(ctxt, id)
 
50
            self.compute_api.pause(ctxt, server)
 
51
        except exception.InstanceInvalidState as state_error:
 
52
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 
53
                    'pause')
 
54
        except Exception:
 
55
            readable = traceback.format_exc()
 
56
            LOG.exception(_("Compute.api::pause %s"), readable)
 
57
            raise exc.HTTPUnprocessableEntity()
 
58
        return webob.Response(status_int=202)
 
59
 
 
60
    @wsgi.action('unpause')
 
61
    @exception.novaclient_converter
 
62
    @scheduler_api.redirect_handler
 
63
    def _unpause(self, req, id, body):
 
64
        """Permit Admins to unpause the server"""
 
65
        ctxt = req.environ['nova.context']
 
66
        try:
 
67
            server = self.compute_api.get(ctxt, id)
 
68
            self.compute_api.unpause(ctxt, server)
 
69
        except exception.InstanceInvalidState as state_error:
 
70
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 
71
                    'unpause')
 
72
        except Exception:
 
73
            readable = traceback.format_exc()
 
74
            LOG.exception(_("Compute.api::unpause %s"), readable)
 
75
            raise exc.HTTPUnprocessableEntity()
 
76
        return webob.Response(status_int=202)
 
77
 
 
78
    @wsgi.action('suspend')
 
79
    @exception.novaclient_converter
 
80
    @scheduler_api.redirect_handler
 
81
    def _suspend(self, req, id, body):
 
82
        """Permit admins to suspend the server"""
 
83
        context = req.environ['nova.context']
 
84
        try:
 
85
            server = self.compute_api.get(context, id)
 
86
            self.compute_api.suspend(context, server)
 
87
        except exception.InstanceInvalidState as state_error:
 
88
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 
89
                    'suspend')
 
90
        except Exception:
 
91
            readable = traceback.format_exc()
 
92
            LOG.exception(_("compute.api::suspend %s"), readable)
 
93
            raise exc.HTTPUnprocessableEntity()
 
94
        return webob.Response(status_int=202)
 
95
 
 
96
    @wsgi.action('resume')
 
97
    @exception.novaclient_converter
 
98
    @scheduler_api.redirect_handler
 
99
    def _resume(self, req, id, body):
 
100
        """Permit admins to resume the server from suspend"""
 
101
        context = req.environ['nova.context']
 
102
        try:
 
103
            server = self.compute_api.get(context, id)
 
104
            self.compute_api.resume(context, server)
 
105
        except exception.InstanceInvalidState as state_error:
 
106
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 
107
                    'resume')
 
108
        except Exception:
 
109
            readable = traceback.format_exc()
 
110
            LOG.exception(_("compute.api::resume %s"), readable)
 
111
            raise exc.HTTPUnprocessableEntity()
 
112
        return webob.Response(status_int=202)
 
113
 
 
114
    @wsgi.action('migrate')
 
115
    @exception.novaclient_converter
 
116
    @scheduler_api.redirect_handler
 
117
    def _migrate(self, req, id, body):
 
118
        """Permit admins to migrate a server to a new host"""
 
119
        context = req.environ['nova.context']
 
120
        try:
 
121
            instance = self.compute_api.get(context, id)
 
122
            self.compute_api.resize(req.environ['nova.context'], instance)
 
123
        except exception.InstanceInvalidState as state_error:
 
124
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 
125
                    'migrate')
 
126
        except Exception, e:
 
127
            LOG.exception(_("Error in migrate %s"), e)
 
128
            raise exc.HTTPBadRequest()
 
129
        return webob.Response(status_int=202)
 
130
 
 
131
    @wsgi.action('resetNetwork')
 
132
    @exception.novaclient_converter
 
133
    @scheduler_api.redirect_handler
 
134
    def _reset_network(self, req, id, body):
 
135
        """Permit admins to reset networking on an server"""
 
136
        context = req.environ['nova.context']
 
137
        try:
 
138
            instance = self.compute_api.get(context, id)
 
139
            self.compute_api.reset_network(context, instance)
 
140
        except Exception:
 
141
            readable = traceback.format_exc()
 
142
            LOG.exception(_("Compute.api::reset_network %s"), readable)
 
143
            raise exc.HTTPUnprocessableEntity()
 
144
        return webob.Response(status_int=202)
 
145
 
 
146
    @wsgi.action('injectNetworkInfo')
 
147
    @exception.novaclient_converter
 
148
    @scheduler_api.redirect_handler
 
149
    def _inject_network_info(self, req, id, body):
 
150
        """Permit admins to inject network info into a server"""
 
151
        context = req.environ['nova.context']
 
152
        try:
 
153
            instance = self.compute_api.get(context, id)
 
154
            self.compute_api.inject_network_info(context, instance)
 
155
        except exception.InstanceNotFound:
 
156
            raise exc.HTTPNotFound(_("Server not found"))
 
157
        except Exception:
 
158
            readable = traceback.format_exc()
 
159
            LOG.exception(_("Compute.api::inject_network_info %s"), readable)
 
160
            raise exc.HTTPUnprocessableEntity()
 
161
        return webob.Response(status_int=202)
 
162
 
 
163
    @wsgi.action('lock')
 
164
    @exception.novaclient_converter
 
165
    @scheduler_api.redirect_handler
 
166
    def _lock(self, req, id, body):
 
167
        """Permit admins to lock a server"""
 
168
        context = req.environ['nova.context']
 
169
        try:
 
170
            instance = self.compute_api.get(context, id)
 
171
            self.compute_api.lock(context, instance)
 
172
        except exception.InstanceNotFound:
 
173
            raise exc.HTTPNotFound(_("Server not found"))
 
174
        except Exception:
 
175
            readable = traceback.format_exc()
 
176
            LOG.exception(_("Compute.api::lock %s"), readable)
 
177
            raise exc.HTTPUnprocessableEntity()
 
178
        return webob.Response(status_int=202)
 
179
 
 
180
    @wsgi.action('unlock')
 
181
    @exception.novaclient_converter
 
182
    @scheduler_api.redirect_handler
 
183
    def _unlock(self, req, id, body):
 
184
        """Permit admins to lock a server"""
 
185
        context = req.environ['nova.context']
 
186
        try:
 
187
            instance = self.compute_api.get(context, id)
 
188
            self.compute_api.unlock(context, instance)
 
189
        except exception.InstanceNotFound:
 
190
            raise exc.HTTPNotFound(_("Server not found"))
 
191
        except Exception:
 
192
            readable = traceback.format_exc()
 
193
            LOG.exception(_("Compute.api::unlock %s"), readable)
 
194
            raise exc.HTTPUnprocessableEntity()
 
195
        return webob.Response(status_int=202)
 
196
 
 
197
    @wsgi.action('createBackup')
 
198
    def _create_backup(self, req, id, body):
 
199
        """Backup a server instance.
 
200
 
 
201
        Images now have an `image_type` associated with them, which can be
 
202
        'snapshot' or the backup type, like 'daily' or 'weekly'.
 
203
 
 
204
        If the image_type is backup-like, then the rotation factor can be
 
205
        included and that will cause the oldest backups that exceed the
 
206
        rotation factor to be deleted.
 
207
 
 
208
        """
 
209
        context = req.environ["nova.context"]
 
210
 
 
211
        try:
 
212
            entity = body["createBackup"]
 
213
        except (KeyError, TypeError):
 
214
            raise exc.HTTPBadRequest(_("Malformed request body"))
 
215
 
 
216
        try:
 
217
            image_name = entity["name"]
 
218
            backup_type = entity["backup_type"]
 
219
            rotation = entity["rotation"]
 
220
 
 
221
        except KeyError as missing_key:
 
222
            msg = _("createBackup entity requires %s attribute") % missing_key
 
223
            raise exc.HTTPBadRequest(explanation=msg)
 
224
 
 
225
        except TypeError:
 
226
            msg = _("Malformed createBackup entity")
 
227
            raise exc.HTTPBadRequest(explanation=msg)
 
228
 
 
229
        try:
 
230
            rotation = int(rotation)
 
231
        except ValueError:
 
232
            msg = _("createBackup attribute 'rotation' must be an integer")
 
233
            raise exc.HTTPBadRequest(explanation=msg)
 
234
 
 
235
        props = {}
 
236
        metadata = entity.get('metadata', {})
 
237
        common.check_img_metadata_quota_limit(context, metadata)
 
238
        try:
 
239
            props.update(metadata)
 
240
        except ValueError:
 
241
            msg = _("Invalid metadata")
 
242
            raise exc.HTTPBadRequest(explanation=msg)
 
243
 
 
244
        try:
 
245
            instance = self.compute_api.get(context, id)
 
246
        except exception.NotFound:
 
247
            raise exc.HTTPNotFound(_("Instance not found"))
 
248
 
 
249
        try:
 
250
            image = self.compute_api.backup(context, instance, image_name,
 
251
                    backup_type, rotation, extra_properties=props)
 
252
        except exception.InstanceInvalidState as state_error:
 
253
            common.raise_http_conflict_for_instance_invalid_state(state_error,
 
254
                    'createBackup')
 
255
 
 
256
        # build location of newly-created image entity
 
257
        image_id = str(image['id'])
 
258
        image_ref = os.path.join(req.application_url, 'images', image_id)
 
259
 
 
260
        resp = webob.Response(status_int=202)
 
261
        resp.headers['Location'] = image_ref
 
262
        return resp
 
263
 
 
264
 
 
265
class Admin_actions(extensions.ExtensionDescriptor):
 
266
    """Enable admin-only server actions
 
267
 
 
268
    Actions include: pause, unpause, suspend, resume, migrate,
 
269
    resetNetwork, injectNetworkInfo, lock, unlock, createBackup
 
270
    """
 
271
 
 
272
    name = "AdminActions"
 
273
    alias = "os-admin-actions"
 
274
    namespace = "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"
 
275
    updated = "2011-09-20T00:00:00+00:00"
 
276
    admin_only = True
 
277
 
 
278
    def get_controller_extensions(self):
 
279
        controller = AdminActionsController()
 
280
        extension = extensions.ControllerExtension(self, 'servers', controller)
 
281
        return [extension]