Revision 529178b1 api/actions.py

b/api/actions.py
9 9
from django.template.loader import render_to_string
10 10
from django.utils import simplejson as json
11 11

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

  
19 19

  
20 20
server_actions = {}
21 21

  
22
rapi = GanetiRapiClient(*settings.GANETI_CLUSTER_INFO)
23

  
24 22

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

  
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 34

  
118 35
@server_action('changePassword')
119 36
def change_password(request, vm, args):
......
128 45
    #                       overLimit (413)
129 46
    
130 47
    try:
131
        adminPass = args['adminPass']
48
        password = args['adminPass']
132 49
    except KeyError:
133
        raise BadRequest()
50
        raise BadRequest('Malformed request.')
134 51

  
135
    raise ServiceUnavailable()
52
    raise ServiceUnavailable('Changing password is not supported.')
136 53

  
137 54
@server_action('reboot')
138 55
def reboot(request, vm, args):
......
148 65
    
149 66
    reboot_type = args.get('type', '')
150 67
    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())
68
        raise BadRequest('Malformed Request.')
69
    reboot_instance(vm, reboot_type.lower())
155 70
    return HttpResponse(status=202)
156 71

  
157 72
@server_action('start')
......
159 74
    # Normal Response Code: 202
160 75
    # Error Response Codes: serviceUnavailable (503),
161 76
    #                       itemNotFound (404)
162

  
163
    backend.start_action(vm, 'START')
164
    rapi.StartupInstance(vm.backend_id)
77
    
78
    if args:
79
        raise BadRequest('Malformed Request.')
80
    startup_instance(vm)
165 81
    return HttpResponse(status=202)
166 82

  
167 83
@server_action('shutdown')
......
170 86
    # Error Response Codes: serviceUnavailable (503),
171 87
    #                       itemNotFound (404)
172 88
    
173
    backend.start_action(vm, 'STOP')
174
    rapi.ShutdownInstance(vm.backend_id)
89
    if args:
90
        raise BadRequest('Malformed Request.')
91
    shutdown_instance(vm)
175 92
    return HttpResponse(status=202)
176 93

  
177 94
@server_action('rebuild')
......
187 104
    #                       serverCapacityUnavailable (503),
188 105
    #                       overLimit (413)
189 106

  
190
    raise ServiceUnavailable()
107
    raise ServiceUnavailable('Rebuild not supported.')
191 108

  
192 109
@server_action('resize')
193 110
def resize(request, vm, args):
......
203 120
    #                       overLimit (413),
204 121
    #                       resizeNotAllowed (403)
205 122
    
206
    raise ResizeNotAllowed()
123
    raise ServiceUnavailable('Resize not supported.')
207 124

  
208 125
@server_action('confirmResize')
209 126
def confirm_resize(request, vm, args):
......
219 136
    #                       overLimit (413),
220 137
    #                       resizeNotAllowed (403)
221 138
    
222
    raise ResizeNotAllowed()
139
    raise ServiceUnavailable('Resize not supported.')
223 140

  
224 141
@server_action('revertResize')
225 142
def revert_resize(request, vm, args):
......
235 152
    #                       overLimit (413),
236 153
    #                       resizeNotAllowed (403)
237 154

  
238
    raise ResizeNotAllowed()
155
    raise ServiceUnavailable('Resize not supported.')
156

  
157
@server_action('console')
158
def get_console(request, vm, args):
159
    """Arrange for an OOB console of the specified type
160

  
161
    This method arranges for an OOB console of the specified type.
162
    Only consoles of type "vnc" are supported for now.
163

  
164
    It uses a running instance of vncauthproxy to setup proper
165
    VNC forwarding with a random password, then returns the necessary
166
    VNC connection info to the caller.
167
    """
168
    # Normal Response Code: 200
169
    # Error Response Codes: computeFault (400, 500),
170
    #                       serviceUnavailable (503),
171
    #                       unauthorized (401),
172
    #                       badRequest (400),
173
    #                       badMediaType(415),
174
    #                       itemNotFound (404),
175
    #                       buildInProgress (409),
176
    #                       overLimit (413)
177
    
178
    console_type = args.get('type', '')
179
    if console_type != 'vnc':
180
        raise BadRequest('Type can only be "vnc".')
181

  
182
    # Use RAPI to get VNC console information for this instance
183
    if get_rsapi_state(vm) != 'ACTIVE':
184
        raise BadRequest('Server not in ACTIVE state.')
185
    
186
    if settings.TEST:
187
        console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
188
    else:
189
        console_data = get_instance_console(vm)
190
    
191
    if console_data['kind'] != 'vnc':
192
        raise ServiceUnavailable('Could not create a console of requested type.')
193
    
194
    # Let vncauthproxy decide on the source port.
195
    # The alternative: static allocation, e.g.
196
    # sport = console_data['port'] - 1000
197
    sport = 0
198
    daddr = console_data['host']
199
    dport = console_data['port']
200
    password = random_password()
201
    
202
    try:
203
        if settings.TEST:
204
            fwd = {'source_port': 1234, 'status': 'OK'}
205
        else:
206
            fwd = request_vnc_forwarding(sport, daddr, dport, password)
207
    except Exception:
208
        raise ServiceUnavailable('Could not allocate VNC console port.')
209

  
210
    if fwd['status'] != "OK":
211
        raise ServiceUnavailable('Could not allocate VNC console.')
212
    
213
    console = {
214
        'type': 'vnc',
215
        'host': getfqdn(),
216
        'port': fwd['source_port'],
217
        'password': password}
218
    
219
    if request.serialization == 'xml':
220
        mimetype = 'application/xml'
221
        data = render_to_string('console.xml', {'console': console})
222
    else:
223
        mimetype = 'application/json'
224
        data = json.dumps({'console': console})
225
    
226
    return HttpResponse(data, mimetype=mimetype, status=200)

Also available in: Unified diff