Revision bcd80cd9 snf-cyclades-app/synnefo/api/servers.py
b/snf-cyclades-app/synnefo/api/servers.py | ||
---|---|---|
245 | 245 |
|
246 | 246 |
|
247 | 247 |
@api.api_method(http_method='POST', user_required=True, logger=log) |
248 |
# Use manual transactions. Backend and IP pool allocations need exclusive |
|
249 |
# access (SELECT..FOR UPDATE). Running create_server with commit_on_success |
|
250 |
# would result in backends and public networks to be locked until the job is |
|
251 |
# sent to the Ganeti backend. |
|
252 |
@transaction.commit_manually |
|
253 | 248 |
def create_server(request): |
254 | 249 |
# Normal Response Code: 202 |
255 | 250 |
# Error Response Codes: computeFault (400, 500), |
... | ... | |
260 | 255 |
# badRequest (400), |
261 | 256 |
# serverCapacityUnavailable (503), |
262 | 257 |
# overLimit (413) |
258 |
req = utils.get_request_dict(request) |
|
259 |
log.info('create_server %s', req) |
|
260 |
user_id = request.user_uniq |
|
261 |
|
|
263 | 262 |
try: |
264 |
req = utils.get_request_dict(request) |
|
265 |
log.info('create_server %s', req) |
|
266 |
user_id = request.user_uniq |
|
263 |
server = req['server'] |
|
264 |
name = server['name'] |
|
265 |
metadata = server.get('metadata', {}) |
|
266 |
assert isinstance(metadata, dict) |
|
267 |
image_id = server['imageRef'] |
|
268 |
flavor_id = server['flavorRef'] |
|
269 |
personality = server.get('personality', []) |
|
270 |
assert isinstance(personality, list) |
|
271 |
except (KeyError, AssertionError): |
|
272 |
raise faults.BadRequest("Malformed request") |
|
273 |
|
|
274 |
# Verify that personalities are well-formed |
|
275 |
util.verify_personality(personality) |
|
276 |
# Get image information |
|
277 |
image = util.get_image_dict(image_id, user_id) |
|
278 |
# Get flavor (ensure it is active) |
|
279 |
flavor = util.get_flavor(flavor_id, include_deleted=False) |
|
280 |
# Generate password |
|
281 |
password = util.random_password() |
|
282 |
|
|
283 |
vm = do_create_server(user_id, name, password, flavor, image, |
|
284 |
metadata=metadata, personality=personality) |
|
285 |
|
|
286 |
server = vm_to_dict(vm, detail=True) |
|
287 |
server['status'] = 'BUILD' |
|
288 |
server['adminPass'] = password |
|
289 |
|
|
290 |
response = render_server(request, server, status=202) |
|
291 |
|
|
292 |
return response |
|
293 |
|
|
267 | 294 |
|
295 |
@transaction.commit_manually |
|
296 |
def do_create_server(userid, name, password, flavor, image, metadata={}, |
|
297 |
personality=[], network=None, backend=None): |
|
298 |
if backend is None: |
|
299 |
# Allocate backend to host the server. Commit after allocation to |
|
300 |
# release the locks hold by the backend allocator. |
|
268 | 301 |
try: |
269 |
server = req['server'] |
|
270 |
name = server['name'] |
|
271 |
metadata = server.get('metadata', {}) |
|
272 |
assert isinstance(metadata, dict) |
|
273 |
image_id = server['imageRef'] |
|
274 |
flavor_id = server['flavorRef'] |
|
275 |
personality = server.get('personality', []) |
|
276 |
assert isinstance(personality, list) |
|
277 |
except (KeyError, AssertionError): |
|
278 |
raise faults.BadRequest("Malformed request") |
|
279 |
|
|
280 |
# Verify that personalities are well-formed |
|
281 |
util.verify_personality(personality) |
|
282 |
# Get image information |
|
283 |
image = util.get_image_dict(image_id, user_id) |
|
284 |
# Get flavor (ensure it is active) |
|
285 |
flavor = util.get_flavor(flavor_id, include_deleted=False) |
|
286 |
# Allocate VM to backend |
|
287 |
backend_allocator = BackendAllocator() |
|
288 |
backend = backend_allocator.allocate(request.user_uniq, flavor) |
|
289 |
|
|
290 |
if backend is None: |
|
291 |
log.error("No available backends for VM with flavor %s", flavor) |
|
292 |
raise faults.ServiceUnavailable("No available backends") |
|
293 |
except: |
|
294 |
transaction.rollback() |
|
295 |
raise |
|
296 |
else: |
|
297 |
transaction.commit() |
|
302 |
backend_allocator = BackendAllocator() |
|
303 |
backend = backend_allocator.allocate(userid, flavor) |
|
304 |
if backend is None: |
|
305 |
log.error("No available backend for VM with flavor %s", flavor) |
|
306 |
raise faults.ServiceUnavailable("No available backends") |
|
307 |
except: |
|
308 |
transaction.rollback() |
|
309 |
raise |
|
310 |
else: |
|
311 |
transaction.commit() |
|
298 | 312 |
|
299 | 313 |
# Fix flavor for archipelago |
300 |
password = util.random_password() |
|
301 | 314 |
disk_template, provider = util.get_flavor_provider(flavor) |
302 | 315 |
if provider: |
303 | 316 |
flavor.disk_template = disk_template |
... | ... | |
310 | 323 |
flavor.disk_provider = None |
311 | 324 |
|
312 | 325 |
try: |
313 |
# Allocate IP from public network |
|
314 |
(network, address) = util.get_public_ip(backend) |
|
315 |
nic = {'ip': address, 'network': network.backend_id} |
|
326 |
if network is None: |
|
327 |
# Allocate IP from public network |
|
328 |
(network, address) = util.get_public_ip(backend) |
|
329 |
nic = {'ip': address, 'network': network.backend_id} |
|
330 |
else: |
|
331 |
address = util.get_network_free_address(network) |
|
316 | 332 |
|
317 | 333 |
# We must save the VM instance now, so that it gets a valid |
318 | 334 |
# vm.backend_vm_id. |
319 | 335 |
vm = VirtualMachine.objects.create( |
320 | 336 |
name=name, |
321 | 337 |
backend=backend, |
322 |
userid=user_id,
|
|
323 |
imageid=image_id,
|
|
338 |
userid=userid, |
|
339 |
imageid=image["id"],
|
|
324 | 340 |
flavor=flavor, |
325 | 341 |
action="CREATE") |
326 | 342 |
|
... | ... | |
364 | 380 |
vm.save() |
365 | 381 |
transaction.commit() |
366 | 382 |
log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s", |
367 |
user_id, vm, nic, backend, str(jobID))
|
|
383 |
userid, vm, nic, backend, str(jobID)) |
|
368 | 384 |
except GanetiApiError as e: |
369 | 385 |
log.exception("Can not communicate to backend %s: %s.", |
370 | 386 |
backend, e) |
... | ... | |
380 | 396 |
transaction.rollback() |
381 | 397 |
raise |
382 | 398 |
|
383 |
server = vm_to_dict(vm, detail=True) |
|
384 |
server['status'] = 'BUILD' |
|
385 |
server['adminPass'] = password |
|
386 |
|
|
387 |
response = render_server(request, server, status=202) |
|
388 |
|
|
389 |
return response |
|
399 |
return vm |
|
390 | 400 |
|
391 | 401 |
|
392 | 402 |
@api.api_method(http_method='GET', user_required=True, logger=log) |
Also available in: Unified diff