Statistics
| Branch: | Tag: | Revision:

root / api / actions.py @ b36f78fa

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 import backend
16
from synnefo.logic.utils import get_rsapi_state
17

    
18

    
19
server_actions = {}
20
network_actions = {}
21

    
22

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

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

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

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

    
43

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
200
    if console_data['kind'] != 'vnc':
201
        message = 'Could not create a console of requested type.'
202
        raise ServiceUnavailable(message)
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
    backend.connect_to_network(vm, net)
255
    vm.save()
256
    net.save()
257
    return HttpResponse(status=202)
258

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

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