Statistics
| Branch: | Tag: | Revision:

root / api / actions.py @ aa197ee4

History | View | Annotate | Download (9.3 kB)

1
#
2
# Copyright (c) 2010 Greek Research and Technology Network
3
#
4

    
5
from socket import getfqdn
6

    
7
from django.conf import settings
8
from django.http import HttpResponse
9
from django.template.loader import render_to_string
10
from django.utils import simplejson as json
11

    
12
from synnefo.api.faults import BadRequest, ServiceUnavailable
13
from synnefo.api.util import random_password, get_vm
14
from synnefo.util.vapclient import request_forwarding as request_vnc_forwarding
15
from synnefo.logic.backend import (reboot_instance, startup_instance, shutdown_instance,
16
                                    get_instance_console)
17
from synnefo.logic.utils import get_rsapi_state
18

    
19

    
20
server_actions = {}
21
network_actions = {}
22

    
23

    
24
def server_action(name):
25
    '''Decorator for functions implementing server actions.
26
    `name` is the key in the dict passed by the client.
27
    '''
28

    
29
    def decorator(func):
30
        server_actions[name] = func
31
        return func
32
    return decorator
33

    
34
def network_action(name):
35
    '''Decorator for functions implementing network actions.
36
    `name` is the key in the dict passed by the client.
37
    '''
38

    
39
    def decorator(func):
40
        network_actions[name] = func
41
        return func
42
    return decorator
43

    
44

    
45
@server_action('changePassword')
46
def change_password(request, vm, args):
47
    # Normal Response Code: 202
48
    # Error Response Codes: computeFault (400, 500),
49
    #                       serviceUnavailable (503),
50
    #                       unauthorized (401),
51
    #                       badRequest (400),
52
    #                       badMediaType(415),
53
    #                       itemNotFound (404),
54
    #                       buildInProgress (409),
55
    #                       overLimit (413)
56

    
57
    try:
58
        password = args['adminPass']
59
    except KeyError:
60
        raise BadRequest('Malformed request.')
61

    
62
    raise ServiceUnavailable('Changing password is not supported.')
63

    
64
@server_action('reboot')
65
def reboot(request, vm, args):
66
    # Normal Response Code: 202
67
    # Error Response Codes: computeFault (400, 500),
68
    #                       serviceUnavailable (503),
69
    #                       unauthorized (401),
70
    #                       badRequest (400),
71
    #                       badMediaType(415),
72
    #                       itemNotFound (404),
73
    #                       buildInProgress (409),
74
    #                       overLimit (413)
75

    
76
    reboot_type = args.get('type', '')
77
    if reboot_type not in ('SOFT', 'HARD'):
78
        raise BadRequest('Malformed Request.')
79
    reboot_instance(vm, reboot_type.lower())
80
    return HttpResponse(status=202)
81

    
82
@server_action('start')
83
def start(request, vm, args):
84
    # Normal Response Code: 202
85
    # Error Response Codes: serviceUnavailable (503),
86
    #                       itemNotFound (404)
87

    
88
    if args:
89
        raise BadRequest('Malformed Request.')
90
    startup_instance(vm)
91
    return HttpResponse(status=202)
92

    
93
@server_action('shutdown')
94
def shutdown(request, vm, args):
95
    # Normal Response Code: 202
96
    # Error Response Codes: serviceUnavailable (503),
97
    #                       itemNotFound (404)
98

    
99
    if args:
100
        raise BadRequest('Malformed Request.')
101
    shutdown_instance(vm)
102
    return HttpResponse(status=202)
103

    
104
@server_action('rebuild')
105
def rebuild(request, vm, args):
106
    # Normal Response Code: 202
107
    # Error Response Codes: computeFault (400, 500),
108
    #                       serviceUnavailable (503),
109
    #                       unauthorized (401),
110
    #                       badRequest (400),
111
    #                       badMediaType(415),
112
    #                       itemNotFound (404),
113
    #                       buildInProgress (409),
114
    #                       serverCapacityUnavailable (503),
115
    #                       overLimit (413)
116

    
117
    raise ServiceUnavailable('Rebuild not supported.')
118

    
119
@server_action('resize')
120
def resize(request, vm, args):
121
    # Normal Response Code: 202
122
    # Error Response Codes: computeFault (400, 500),
123
    #                       serviceUnavailable (503),
124
    #                       unauthorized (401),
125
    #                       badRequest (400),
126
    #                       badMediaType(415),
127
    #                       itemNotFound (404),
128
    #                       buildInProgress (409),
129
    #                       serverCapacityUnavailable (503),
130
    #                       overLimit (413),
131
    #                       resizeNotAllowed (403)
132

    
133
    raise ServiceUnavailable('Resize not supported.')
134

    
135
@server_action('confirmResize')
136
def confirm_resize(request, vm, args):
137
    # Normal Response Code: 204
138
    # Error Response Codes: computeFault (400, 500),
139
    #                       serviceUnavailable (503),
140
    #                       unauthorized (401),
141
    #                       badRequest (400),
142
    #                       badMediaType(415),
143
    #                       itemNotFound (404),
144
    #                       buildInProgress (409),
145
    #                       serverCapacityUnavailable (503),
146
    #                       overLimit (413),
147
    #                       resizeNotAllowed (403)
148

    
149
    raise ServiceUnavailable('Resize not supported.')
150

    
151
@server_action('revertResize')
152
def revert_resize(request, vm, args):
153
    # Normal Response Code: 202
154
    # Error Response Codes: computeFault (400, 500),
155
    #                       serviceUnavailable (503),
156
    #                       unauthorized (401),
157
    #                       badRequest (400),
158
    #                       badMediaType(415),
159
    #                       itemNotFound (404),
160
    #                       buildInProgress (409),
161
    #                       serverCapacityUnavailable (503),
162
    #                       overLimit (413),
163
    #                       resizeNotAllowed (403)
164

    
165
    raise ServiceUnavailable('Resize not supported.')
166

    
167
@server_action('console')
168
def get_console(request, vm, args):
169
    """Arrange for an OOB console of the specified type
170

171
    This method arranges for an OOB console of the specified type.
172
    Only consoles of type "vnc" are supported for now.
173

174
    It uses a running instance of vncauthproxy to setup proper
175
    VNC forwarding with a random password, then returns the necessary
176
    VNC connection info to the caller.
177
    """
178
    # Normal Response Code: 200
179
    # Error Response Codes: computeFault (400, 500),
180
    #                       serviceUnavailable (503),
181
    #                       unauthorized (401),
182
    #                       badRequest (400),
183
    #                       badMediaType(415),
184
    #                       itemNotFound (404),
185
    #                       buildInProgress (409),
186
    #                       overLimit (413)
187

    
188
    console_type = args.get('type', '')
189
    if console_type != 'vnc':
190
        raise BadRequest('Type can only be "vnc".')
191

    
192
    # Use RAPI to get VNC console information for this instance
193
    if get_rsapi_state(vm) != 'ACTIVE':
194
        raise BadRequest('Server not in ACTIVE state.')
195

    
196
    if settings.TEST:
197
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
198
    else:
199
        console_data = get_instance_console(vm)
200

    
201
    if console_data['kind'] != 'vnc':
202
        raise ServiceUnavailable('Could not create a console of requested type.')
203

    
204
    # Let vncauthproxy decide on the source port.
205
    # The alternative: static allocation, e.g.
206
    # sport = console_data['port'] - 1000
207
    sport = 0
208
    daddr = console_data['host']
209
    dport = console_data['port']
210
    password = random_password()
211

    
212
    try:
213
        if settings.TEST:
214
            fwd = {'source_port': 1234, 'status': 'OK'}
215
        else:
216
            fwd = request_vnc_forwarding(sport, daddr, dport, password)
217
    except Exception:
218
        raise ServiceUnavailable('Could not allocate VNC console port.')
219

    
220
    if fwd['status'] != "OK":
221
        raise ServiceUnavailable('Could not allocate VNC console.')
222

    
223
    console = {
224
        'type': 'vnc',
225
        'host': getfqdn(),
226
        'port': fwd['source_port'],
227
        'password': password}
228

    
229
    if request.serialization == 'xml':
230
        mimetype = 'application/xml'
231
        data = render_to_string('console.xml', {'console': console})
232
    else:
233
        mimetype = 'application/json'
234
        data = json.dumps({'console': console})
235

    
236
    return HttpResponse(data, mimetype=mimetype, status=200)
237

    
238

    
239
@network_action('add')
240
def add(request, net, args):
241
    # Normal Response Code: 202
242
    # Error Response Codes: computeFault (400, 500),
243
    #                       serviceUnavailable (503),
244
    #                       unauthorized (401),
245
    #                       badRequest (400),
246
    #                       badMediaType(415),
247
    #                       itemNotFound (404),
248
    #                       overLimit (413)
249

    
250
    server_id = args.get('serverRef', None)
251
    if not server_id:
252
        raise BadRequest('Malformed Request.')
253
    vm = get_vm(server_id, request.user)
254
    net.machines.add(vm)
255
    net.save()
256
    return HttpResponse(status=202)
257

    
258
@network_action('remove')
259
def remove(request, net, args):
260
    # Normal Response Code: 202
261
    # Error Response Codes: computeFault (400, 500),
262
    #                       serviceUnavailable (503),
263
    #                       unauthorized (401),
264
    #                       badRequest (400),
265
    #                       badMediaType(415),
266
    #                       itemNotFound (404),
267
    #                       overLimit (413)
268

    
269
    server_id = args.get('serverRef', None)
270
    if not server_id:
271
        raise BadRequest('Malformed Request.')
272
    vm = get_vm(server_id, request.user)
273
    net.machines.remove(vm)
274
    net.save()
275
    return HttpResponse(status=202)