Statistics
| Branch: | Tag: | Revision:

root / api / actions.py @ 4cf8adf8

History | View | Annotate | Download (7.9 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, ResizeNotAllowed, ServiceUnavailable
13
from synnefo.api.util import random_password
14
from synnefo.util.rapi import GanetiRapiClient
15
from synnefo.util.vapclient import request_forwarding as request_vnc_forwarding
16
from synnefo.logic import backend
17
from synnefo.logic.utils import get_rsapi_state
18

    
19

    
20
server_actions = {}
21

    
22
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
23

    
24

    
25
def server_action(name):
26
    '''Decorator for functions implementing server actions.
27
    
28
       `name` is the key in the dict passed by the client.
29
    '''
30
    
31
    def decorator(func):
32
        server_actions[name] = func
33
        return func
34
    return decorator
35

    
36
@server_action('console')
37
def get_console(request, vm, args):
38
    """Arrange for an OOB console of the specified type
39

40
    This method arranges for an OOB console of the specified type.
41
    Only consoles of type "vnc" are supported for now.
42
    
43
    It uses a running instance of vncauthproxy to setup proper
44
    VNC forwarding with a random password, then returns the necessary
45
    VNC connection info to the caller.
46

47
    JSON Request: {
48
        "console": {
49
            "type": "vnc"
50
        }
51
    }
52

53
    JSON Reply: {
54
        "vnc": {
55
            "host": "fqdn_here",
56
            "port": a_port_here,
57
            "password": "a_password_here"
58
        }
59
    }
60

61
    """
62
    # Normal Response Code: 200
63
    # Error Response Codes: computeFault (400, 500),
64
    #                       serviceUnavailable (503),
65
    #                       unauthorized (401),
66
    #                       badRequest (400),
67
    #                       badMediaType(415),
68
    #                       itemNotFound (404),
69
    #                       buildInProgress (409),
70
    #                       overLimit (413)
71
    try:
72
        console_type = args.get('type', '')
73
        if console_type != 'vnc':
74
            raise BadRequest(message="type can only be 'vnc'")
75
    except KeyError:
76
        raise BadRequest()
77

    
78
    # Use RAPI to get VNC console information for this instance
79
    if get_rsapi_state(vm) != 'ACTIVE':
80
        raise BadRequest(message="Server not in ACTIVE state")
81
    if settings.TEST:
82
        console_data = { 'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000 }
83
    else:
84
        console_data = rapi.GetInstanceConsole(vm.backend_id)
85
    if console_data['kind'] != 'vnc':
86
        raise ServiceUnavailable()
87

    
88
    # Let vncauthproxy decide on the source port.
89
    # The alternative: static allocation, e.g.
90
    # sport = console_data['port'] - 1000
91
    sport = 0
92
    daddr = console_data['host']
93
    dport = console_data['port']
94
    passwd = random_password()
95

    
96
    try:
97
        if settings.TEST:
98
            fwd = { 'source_port': 1234, 'status': 'OK' }
99
        else:
100
            fwd = request_vnc_forwarding(sport, daddr, dport, passwd)
101
        if fwd['status'] != "OK":
102
            raise ServiceUnavailable()
103
        vnc = { 'host': getfqdn(), 'port': fwd['source_port'], 'password': passwd }
104
    except Exception:
105
        raise ServiceUnavailable("Could not allocate VNC console port")
106

    
107
    # Format to be reviewed by [verigak], FIXME
108
    if request.serialization == 'xml':
109
        mimetype = 'application/xml'
110
        data = render_to_string('vnc.xml', {'vnc': vnc})
111
    else:
112
        mimetype = 'application/json'
113
        data = json.dumps({'vnc': vnc})
114

    
115
    return HttpResponse(data, mimetype=mimetype, status=200)
116

    
117

    
118
@server_action('changePassword')
119
def change_password(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
    #                       overLimit (413)
129
    
130
    try:
131
        adminPass = args['adminPass']
132
    except KeyError:
133
        raise BadRequest()
134

    
135
    raise ServiceUnavailable()
136

    
137
@server_action('reboot')
138
def reboot(request, vm, args):
139
    # Normal Response Code: 202
140
    # Error Response Codes: computeFault (400, 500),
141
    #                       serviceUnavailable (503),
142
    #                       unauthorized (401),
143
    #                       badRequest (400),
144
    #                       badMediaType(415),
145
    #                       itemNotFound (404),
146
    #                       buildInProgress (409),
147
    #                       overLimit (413)
148
    
149
    reboot_type = args.get('type', '')
150
    if reboot_type not in ('SOFT', 'HARD'):
151
        raise BadRequest()
152
    
153
    backend.start_action(vm, 'REBOOT')
154
    rapi.RebootInstance(vm.backend_id, reboot_type.lower())
155
    return HttpResponse(status=202)
156

    
157
@server_action('start')
158
def start(request, vm, args):
159
    # Normal Response Code: 202
160
    # Error Response Codes: serviceUnavailable (503),
161
    #                       itemNotFound (404)
162

    
163
    backend.start_action(vm, 'START')
164
    rapi.StartupInstance(vm.backend_id)
165
    return HttpResponse(status=202)
166

    
167
@server_action('shutdown')
168
def shutdown(request, vm, args):
169
    # Normal Response Code: 202
170
    # Error Response Codes: serviceUnavailable (503),
171
    #                       itemNotFound (404)
172
    
173
    backend.start_action(vm, 'STOP')
174
    rapi.ShutdownInstance(vm.backend_id)
175
    return HttpResponse(status=202)
176

    
177
@server_action('rebuild')
178
def rebuild(request, vm, args):
179
    # Normal Response Code: 202
180
    # Error Response Codes: computeFault (400, 500),
181
    #                       serviceUnavailable (503),
182
    #                       unauthorized (401),
183
    #                       badRequest (400),
184
    #                       badMediaType(415),
185
    #                       itemNotFound (404),
186
    #                       buildInProgress (409),
187
    #                       serverCapacityUnavailable (503),
188
    #                       overLimit (413)
189

    
190
    raise ServiceUnavailable()
191

    
192
@server_action('resize')
193
def resize(request, vm, args):
194
    # Normal Response Code: 202
195
    # Error Response Codes: computeFault (400, 500),
196
    #                       serviceUnavailable (503),
197
    #                       unauthorized (401),
198
    #                       badRequest (400),
199
    #                       badMediaType(415),
200
    #                       itemNotFound (404),
201
    #                       buildInProgress (409),
202
    #                       serverCapacityUnavailable (503),
203
    #                       overLimit (413),
204
    #                       resizeNotAllowed (403)
205
    
206
    raise ResizeNotAllowed()
207

    
208
@server_action('confirmResize')
209
def confirm_resize(request, vm, args):
210
    # Normal Response Code: 204
211
    # Error Response Codes: computeFault (400, 500),
212
    #                       serviceUnavailable (503),
213
    #                       unauthorized (401),
214
    #                       badRequest (400),
215
    #                       badMediaType(415),
216
    #                       itemNotFound (404),
217
    #                       buildInProgress (409),
218
    #                       serverCapacityUnavailable (503),
219
    #                       overLimit (413),
220
    #                       resizeNotAllowed (403)
221
    
222
    raise ResizeNotAllowed()
223

    
224
@server_action('revertResize')
225
def revert_resize(request, vm, args):
226
    # Normal Response Code: 202
227
    # Error Response Codes: computeFault (400, 500),
228
    #                       serviceUnavailable (503),
229
    #                       unauthorized (401),
230
    #                       badRequest (400),
231
    #                       badMediaType(415),
232
    #                       itemNotFound (404),
233
    #                       buildInProgress (409),
234
    #                       serverCapacityUnavailable (503),
235
    #                       overLimit (413),
236
    #                       resizeNotAllowed (403)
237

    
238
    raise ResizeNotAllowed()