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