Revision f533f224 api/servers.py
b/api/servers.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 import util |
|
12 | 13 |
from synnefo.api.actions import server_actions |
13 | 14 |
from synnefo.api.common import method_not_allowed |
14 | 15 |
from synnefo.api.faults import BadRequest, ItemNotFound, ServiceUnavailable |
15 |
from synnefo.api.util import (isoformat, isoparse, random_password, |
|
16 |
get_vm, get_vm_meta, get_image, get_flavor, |
|
17 |
get_request_dict, render_metadata, render_meta, api_method) |
|
18 | 16 |
from synnefo.db.models import VirtualMachine, VirtualMachineMetadata |
19 | 17 |
from synnefo.logic.backend import create_instance, delete_instance |
20 | 18 |
from synnefo.logic.utils import get_rsapi_state |
... | ... | |
70 | 68 |
return method_not_allowed(request) |
71 | 69 |
|
72 | 70 |
|
73 |
def address_to_dict(ipfour, ipsix): |
|
74 |
return {'id': 'public', |
|
75 |
'values': [{'version': 4, 'addr': ipfour}, {'version': 6, 'addr': ipsix}]} |
|
71 |
def nic_to_dict(nic): |
|
72 |
network = nic.network |
|
73 |
network_id = str(network.id) if not network.public else 'public' |
|
74 |
d = {'id': network_id, 'name': network.name, 'mac': nic.mac} |
|
75 |
if nic.firewall_profile: |
|
76 |
d['firewallProfile'] = nic.firewall_profile |
|
77 |
if nic.ipv4 or nic.ipv6: |
|
78 |
d['values'] = [] |
|
79 |
if nic.ipv4: |
|
80 |
d['values'].append({'version': 4, 'addr': nic.ipv4}) |
|
81 |
if nic.ipv6: |
|
82 |
d['values'].append({'version': 6, 'addr': nic.ipv6}) |
|
83 |
return d |
|
76 | 84 |
|
77 | 85 |
def metadata_to_dict(vm): |
78 | 86 |
vm_meta = vm.virtualmachinemetadata_set.all() |
... | ... | |
84 | 92 |
d['status'] = get_rsapi_state(vm) |
85 | 93 |
d['progress'] = 100 if get_rsapi_state(vm) == 'ACTIVE' else 0 |
86 | 94 |
d['hostId'] = vm.hostid |
87 |
d['updated'] = isoformat(vm.updated) |
|
88 |
d['created'] = isoformat(vm.created) |
|
95 |
d['updated'] = util.isoformat(vm.updated)
|
|
96 |
d['created'] = util.isoformat(vm.created)
|
|
89 | 97 |
d['flavorRef'] = vm.flavor.id |
90 | 98 |
d['imageRef'] = vm.sourceimage.id |
91 | 99 |
|
... | ... | |
93 | 101 |
if metadata: |
94 | 102 |
d['metadata'] = {'values': metadata} |
95 | 103 |
|
96 |
addresses = [address_to_dict(vm.ipfour, vm.ipsix)]
|
|
97 |
addresses.extend({'id': str(network.id), 'values': []} for network in vm.network_set.all())
|
|
98 |
d['addresses'] = {'values': addresses} |
|
104 |
addresses = [nic_to_dict(nic) for nic in vm.nics.all()]
|
|
105 |
if addresses:
|
|
106 |
d['addresses'] = {'values': addresses}
|
|
99 | 107 |
return d |
100 | 108 |
|
101 | 109 |
|
102 | 110 |
def render_server(request, server, status=200): |
103 | 111 |
if request.serialization == 'xml': |
104 |
data = render_to_string('server.xml', {'server': server, 'is_root': True}) |
|
112 |
data = render_to_string('server.xml', { |
|
113 |
'server': server, |
|
114 |
'is_root': True}) |
|
105 | 115 |
else: |
106 | 116 |
data = json.dumps({'server': server}) |
107 | 117 |
return HttpResponse(data, status=status) |
108 | 118 |
|
109 | 119 |
|
110 |
@api_method('GET') |
|
120 |
@util.api_method('GET')
|
|
111 | 121 |
def list_servers(request, detail=False): |
112 | 122 |
# Normal Response Codes: 200, 203 |
113 | 123 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
116 | 126 |
# badRequest (400), |
117 | 127 |
# overLimit (413) |
118 | 128 |
|
119 |
since = isoparse(request.GET.get('changes-since')) |
|
129 |
since = util.isoparse(request.GET.get('changes-since'))
|
|
120 | 130 |
|
121 | 131 |
if since: |
122 |
user_vms = VirtualMachine.objects.filter(owner=request.user, updated__gte=since) |
|
132 |
user_vms = VirtualMachine.objects.filter(owner=request.user, |
|
133 |
updated__gte=since) |
|
123 | 134 |
if not user_vms: |
124 | 135 |
return HttpResponse(status=304) |
125 | 136 |
else: |
126 |
user_vms = VirtualMachine.objects.filter(owner=request.user, deleted=False) |
|
137 |
user_vms = VirtualMachine.objects.filter(owner=request.user, |
|
138 |
deleted=False) |
|
139 |
|
|
127 | 140 |
servers = [vm_to_dict(server, detail) for server in user_vms] |
128 | 141 |
|
129 | 142 |
if request.serialization == 'xml': |
130 |
data = render_to_string('list_servers.xml', {'servers': servers, 'detail': detail}) |
|
143 |
data = render_to_string('list_servers.xml', { |
|
144 |
'servers': servers, |
|
145 |
'detail': detail}) |
|
131 | 146 |
else: |
132 | 147 |
data = json.dumps({'servers': {'values': servers}}) |
133 | 148 |
|
134 | 149 |
return HttpResponse(data, status=200) |
135 | 150 |
|
136 |
@api_method('POST') |
|
151 |
@util.api_method('POST')
|
|
137 | 152 |
def create_server(request): |
138 | 153 |
# Normal Response Code: 202 |
139 | 154 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
145 | 160 |
# serverCapacityUnavailable (503), |
146 | 161 |
# overLimit (413) |
147 | 162 |
|
148 |
req = get_request_dict(request) |
|
149 |
|
|
163 |
req = util.get_request_dict(request) |
|
164 |
owner = request.user |
|
165 |
|
|
150 | 166 |
try: |
151 | 167 |
server = req['server'] |
152 | 168 |
name = server['name'] |
... | ... | |
156 | 172 |
flavor_id = server['flavorRef'] |
157 | 173 |
except (KeyError, AssertionError): |
158 | 174 |
raise BadRequest('Malformed request.') |
159 |
|
|
160 |
image = get_image(image_id, request.user) |
|
161 |
flavor = get_flavor(flavor_id) |
|
162 |
|
|
175 |
|
|
176 |
image = util.get_image(image_id, owner) |
|
177 |
flavor = util.get_flavor(flavor_id) |
|
178 |
password = util.random_password() |
|
179 |
|
|
163 | 180 |
# We must save the VM instance now, so that it gets a valid vm.backend_id. |
164 | 181 |
vm = VirtualMachine.objects.create( |
165 | 182 |
name=name, |
166 |
owner=request.user,
|
|
183 |
owner=owner,
|
|
167 | 184 |
sourceimage=image, |
168 |
ipfour='0.0.0.0', |
|
169 |
ipsix='::1', |
|
170 | 185 |
flavor=flavor) |
171 |
|
|
172 |
password = random_password() |
|
173 |
|
|
186 |
|
|
174 | 187 |
try: |
175 | 188 |
create_instance(vm, flavor, image, password) |
176 | 189 |
except GanetiApiError: |
... | ... | |
178 | 191 |
raise ServiceUnavailable('Could not create server.') |
179 | 192 |
|
180 | 193 |
for key, val in metadata.items(): |
181 |
VirtualMachineMetadata.objects.create(meta_key=key, meta_value=val, vm=vm) |
|
182 |
|
|
194 |
VirtualMachineMetadata.objects.create( |
|
195 |
meta_key=key, |
|
196 |
meta_value=val, |
|
197 |
vm=vm) |
|
198 |
|
|
183 | 199 |
logging.info('created vm with %s cpus, %s ram and %s storage', |
184 | 200 |
flavor.cpu, flavor.ram, flavor.disk) |
185 | 201 |
|
... | ... | |
188 | 204 |
server['adminPass'] = password |
189 | 205 |
return render_server(request, server, status=202) |
190 | 206 |
|
191 |
@api_method('GET') |
|
207 |
@util.api_method('GET')
|
|
192 | 208 |
def get_server_details(request, server_id): |
193 | 209 |
# Normal Response Codes: 200, 203 |
194 | 210 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
198 | 214 |
# itemNotFound (404), |
199 | 215 |
# overLimit (413) |
200 | 216 |
|
201 |
vm = get_vm(server_id, request.user) |
|
217 |
vm = util.get_vm(server_id, request.user)
|
|
202 | 218 |
server = vm_to_dict(vm, detail=True) |
203 | 219 |
return render_server(request, server) |
204 | 220 |
|
205 |
@api_method('PUT') |
|
221 |
@util.api_method('PUT')
|
|
206 | 222 |
def update_server_name(request, server_id): |
207 | 223 |
# Normal Response Code: 204 |
208 | 224 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
214 | 230 |
# buildInProgress (409), |
215 | 231 |
# overLimit (413) |
216 | 232 |
|
217 |
req = get_request_dict(request) |
|
233 |
req = util.get_request_dict(request)
|
|
218 | 234 |
|
219 | 235 |
try: |
220 | 236 |
name = req['server']['name'] |
221 | 237 |
except (TypeError, KeyError): |
222 | 238 |
raise BadRequest('Malformed request.') |
223 | 239 |
|
224 |
vm = get_vm(server_id, request.user) |
|
240 |
vm = util.get_vm(server_id, request.user)
|
|
225 | 241 |
vm.name = name |
226 | 242 |
vm.save() |
227 | 243 |
|
228 | 244 |
return HttpResponse(status=204) |
229 | 245 |
|
230 |
@api_method('DELETE') |
|
246 |
@util.api_method('DELETE')
|
|
231 | 247 |
def delete_server(request, server_id): |
232 | 248 |
# Normal Response Codes: 204 |
233 | 249 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
238 | 254 |
# buildInProgress (409), |
239 | 255 |
# overLimit (413) |
240 | 256 |
|
241 |
vm = get_vm(server_id, request.user) |
|
257 |
vm = util.get_vm(server_id, request.user)
|
|
242 | 258 |
delete_instance(vm) |
243 | 259 |
return HttpResponse(status=204) |
244 | 260 |
|
245 |
@api_method('POST') |
|
261 |
@util.api_method('POST')
|
|
246 | 262 |
def server_action(request, server_id): |
247 |
vm = get_vm(server_id, request.user) |
|
248 |
req = get_request_dict(request) |
|
263 |
vm = util.get_vm(server_id, request.user)
|
|
264 |
req = util.get_request_dict(request)
|
|
249 | 265 |
if len(req) != 1: |
250 | 266 |
raise BadRequest('Malformed request.') |
251 | 267 |
|
... | ... | |
260 | 276 |
except AssertionError: |
261 | 277 |
raise BadRequest('Invalid argument.') |
262 | 278 |
|
263 |
@api_method('GET') |
|
279 |
@util.api_method('GET')
|
|
264 | 280 |
def list_addresses(request, server_id): |
265 | 281 |
# Normal Response Codes: 200, 203 |
266 | 282 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
269 | 285 |
# badRequest (400), |
270 | 286 |
# overLimit (413) |
271 | 287 |
|
272 |
vm = get_vm(server_id, request.user) |
|
273 |
addresses = [address_to_dict(vm.ipfour, vm.ipsix)]
|
|
274 |
|
|
288 |
vm = util.get_vm(server_id, request.user)
|
|
289 |
addresses = [nic_to_dict(nic) for nic in vm.nics.all()]
|
|
290 |
|
|
275 | 291 |
if request.serialization == 'xml': |
276 | 292 |
data = render_to_string('list_addresses.xml', {'addresses': addresses}) |
277 | 293 |
else: |
... | ... | |
279 | 295 |
|
280 | 296 |
return HttpResponse(data, status=200) |
281 | 297 |
|
282 |
@api_method('GET') |
|
298 |
@util.api_method('GET')
|
|
283 | 299 |
def list_addresses_by_network(request, server_id, network_id): |
284 | 300 |
# Normal Response Codes: 200, 203 |
285 | 301 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
288 | 304 |
# badRequest (400), |
289 | 305 |
# itemNotFound (404), |
290 | 306 |
# overLimit (413) |
291 |
|
|
292 |
vm = get_vm(server_id, request.user)
|
|
293 |
if network_id != 'public':
|
|
294 |
raise ItemNotFound('Unknown network.')
|
|
295 |
|
|
296 |
address = address_to_dict(vm.ipfour, vm.ipsix)
|
|
297 |
|
|
307 |
|
|
308 |
owner = request.user
|
|
309 |
machine = util.get_vm(server_id, owner)
|
|
310 |
network = util.get_network(network_id, owner)
|
|
311 |
nic = util.get_nic(machine, network) |
|
312 |
address = nic_to_dict(nic)
|
|
313 |
|
|
298 | 314 |
if request.serialization == 'xml': |
299 | 315 |
data = render_to_string('address.xml', {'address': address}) |
300 | 316 |
else: |
... | ... | |
302 | 318 |
|
303 | 319 |
return HttpResponse(data, status=200) |
304 | 320 |
|
305 |
@api_method('GET') |
|
321 |
@util.api_method('GET')
|
|
306 | 322 |
def list_metadata(request, server_id): |
307 | 323 |
# Normal Response Codes: 200, 203 |
308 | 324 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
311 | 327 |
# badRequest (400), |
312 | 328 |
# overLimit (413) |
313 | 329 |
|
314 |
vm = get_vm(server_id, request.user) |
|
330 |
vm = util.get_vm(server_id, request.user)
|
|
315 | 331 |
metadata = metadata_to_dict(vm) |
316 |
return render_metadata(request, metadata, use_values=True, status=200) |
|
332 |
return util.render_metadata(request, metadata, use_values=True, status=200)
|
|
317 | 333 |
|
318 |
@api_method('POST') |
|
334 |
@util.api_method('POST')
|
|
319 | 335 |
def update_metadata(request, server_id): |
320 | 336 |
# Normal Response Code: 201 |
321 | 337 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
326 | 342 |
# badMediaType(415), |
327 | 343 |
# overLimit (413) |
328 | 344 |
|
329 |
vm = get_vm(server_id, request.user) |
|
330 |
req = get_request_dict(request) |
|
345 |
vm = util.get_vm(server_id, request.user)
|
|
346 |
req = util.get_request_dict(request)
|
|
331 | 347 |
try: |
332 | 348 |
metadata = req['metadata'] |
333 | 349 |
assert isinstance(metadata, dict) |
... | ... | |
344 | 360 |
updated[key] = val |
345 | 361 |
except VirtualMachineMetadata.DoesNotExist: |
346 | 362 |
pass # Ignore non-existent metadata |
363 |
|
|
364 |
if updated: |
|
365 |
vm.save() |
|
366 |
|
|
367 |
return util.render_metadata(request, updated, status=201) |
|
347 | 368 |
|
348 |
return render_metadata(request, updated, status=201) |
|
349 |
|
|
350 |
@api_method('GET') |
|
369 |
@util.api_method('GET') |
|
351 | 370 |
def get_metadata_item(request, server_id, key): |
352 | 371 |
# Normal Response Codes: 200, 203 |
353 | 372 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
357 | 376 |
# badRequest (400), |
358 | 377 |
# overLimit (413) |
359 | 378 |
|
360 |
vm = get_vm(server_id, request.user) |
|
361 |
meta = get_vm_meta(vm, key) |
|
362 |
return render_meta(request, meta, status=200) |
|
379 |
vm = util.get_vm(server_id, request.user)
|
|
380 |
meta = util.get_vm_meta(vm, key)
|
|
381 |
return util.render_meta(request, meta, status=200)
|
|
363 | 382 |
|
364 |
@api_method('PUT') |
|
383 |
@util.api_method('PUT')
|
|
365 | 384 |
def create_metadata_item(request, server_id, key): |
366 | 385 |
# Normal Response Code: 201 |
367 | 386 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
373 | 392 |
# badMediaType(415), |
374 | 393 |
# overLimit (413) |
375 | 394 |
|
376 |
vm = get_vm(server_id, request.user) |
|
377 |
req = get_request_dict(request) |
|
395 |
vm = util.get_vm(server_id, request.user)
|
|
396 |
req = util.get_request_dict(request)
|
|
378 | 397 |
try: |
379 | 398 |
metadict = req['meta'] |
380 | 399 |
assert isinstance(metadict, dict) |
... | ... | |
382 | 401 |
assert key in metadict |
383 | 402 |
except (KeyError, AssertionError): |
384 | 403 |
raise BadRequest('Malformed request.') |
385 |
|
|
386 |
meta, created = VirtualMachineMetadata.objects.get_or_create(meta_key=key, vm=vm) |
|
404 |
|
|
405 |
meta, created = VirtualMachineMetadata.objects.get_or_create( |
|
406 |
meta_key=key, |
|
407 |
vm=vm) |
|
408 |
|
|
387 | 409 |
meta.meta_value = metadict[key] |
388 | 410 |
meta.save() |
389 |
return render_meta(request, meta, status=201) |
|
411 |
vm.save() |
|
412 |
return util.render_meta(request, meta, status=201) |
|
390 | 413 |
|
391 |
@api_method('DELETE') |
|
414 |
@util.api_method('DELETE')
|
|
392 | 415 |
def delete_metadata_item(request, server_id, key): |
393 | 416 |
# Normal Response Code: 204 |
394 | 417 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
400 | 423 |
# badMediaType(415), |
401 | 424 |
# overLimit (413), |
402 | 425 |
|
403 |
vm = get_vm(server_id, request.user) |
|
404 |
meta = get_vm_meta(vm, key) |
|
426 |
vm = util.get_vm(server_id, request.user)
|
|
427 |
meta = util.get_vm_meta(vm, key)
|
|
405 | 428 |
meta.delete() |
429 |
vm.save() |
|
406 | 430 |
return HttpResponse(status=204) |
Also available in: Unified diff