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