Revision 4b8e03e5

b/snf-cyclades-app/synnefo/api/servers.py
199 199
    #                       badRequest (400),
200 200
    #                       serverCapacityUnavailable (503),
201 201
    #                       overLimit (413)
202
    req = util.get_request_dict(request)
203
    log.info('create_server %s', req)
204

  
205 202
    try:
206
        server = req['server']
207
        name = server['name']
208
        metadata = server.get('metadata', {})
209
        assert isinstance(metadata, dict)
210
        image_id = server['imageRef']
211
        flavor_id = server['flavorRef']
212
        personality = server.get('personality', [])
213
        assert isinstance(personality, list)
214
    except (KeyError, AssertionError):
215
        raise faults.BadRequest("Malformed request")
216

  
217
    if len(personality) > settings.MAX_PERSONALITY:
218
        raise faults.OverLimit("Maximum number of personalities exceeded")
203
        req = util.get_request_dict(request)
204
        log.info('create_server %s', req)
219 205

  
220
    for p in personality:
221
        # Verify that personalities are well-formed
222 206
        try:
223
            assert isinstance(p, dict)
224
            keys = set(p.keys())
225
            allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
226
            assert keys.issubset(allowed)
227
            contents = p['contents']
228
            if len(contents) > settings.MAX_PERSONALITY_SIZE:
229
                # No need to decode if contents already exceed limit
230
                raise faults.OverLimit("Maximum size of personality exceeded")
231
            if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
232
                raise faults.OverLimit("Maximum size of personality exceeded")
233
        except AssertionError:
234
            raise faults.BadRequest("Malformed personality in request")
235

  
236
    image = {}
237
    img = util.get_image(image_id, request.user_uniq)
238
    properties = img.get('properties', {})
239
    image['backend_id'] = img['location']
240
    image['format'] = img['disk_format']
241
    image['metadata'] = dict((key.upper(), val) \
242
                             for key, val in properties.items())
243

  
244
    flavor = util.get_flavor(flavor_id)
245
    password = util.random_password()
246

  
247
    count = VirtualMachine.objects.filter(userid=request.user_uniq,
248
                                          deleted=False).count()
249

  
250
    # get user limit
251
    vms_limit_for_user = \
252
        settings.VMS_USER_QUOTA.get(request.user_uniq,
253
                settings.MAX_VMS_PER_USER)
254

  
255
    if count >= vms_limit_for_user:
256
        raise faults.OverLimit("Server count limit exceeded for your account.")
257

  
258
    backend_allocator = BackendAllocator()
259
    backend = backend_allocator.allocate(flavor)
260

  
261
    if backend is None:
207
            server = req['server']
208
            name = server['name']
209
            metadata = server.get('metadata', {})
210
            assert isinstance(metadata, dict)
211
            image_id = server['imageRef']
212
            flavor_id = server['flavorRef']
213
            personality = server.get('personality', [])
214
            assert isinstance(personality, list)
215
        except (KeyError, AssertionError):
216
            raise faults.BadRequest("Malformed request")
217

  
218
        if len(personality) > settings.MAX_PERSONALITY:
219
            raise faults.OverLimit("Maximum number of personalities exceeded")
220

  
221
        for p in personality:
222
            # Verify that personalities are well-formed
223
            try:
224
                assert isinstance(p, dict)
225
                keys = set(p.keys())
226
                allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
227
                assert keys.issubset(allowed)
228
                contents = p['contents']
229
                if len(contents) > settings.MAX_PERSONALITY_SIZE:
230
                    # No need to decode if contents already exceed limit
231
                    raise faults.OverLimit("Maximum size of personality exceeded")
232
                if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
233
                    raise faults.OverLimit("Maximum size of personality exceeded")
234
            except AssertionError:
235
                raise faults.BadRequest("Malformed personality in request")
236

  
237
        image = {}
238
        img = util.get_image(image_id, request.user_uniq)
239
        properties = img.get('properties', {})
240
        image['backend_id'] = img['location']
241
        image['format'] = img['disk_format']
242
        image['metadata'] = dict((key.upper(), val) \
243
                                 for key, val in properties.items())
244

  
245
        flavor = util.get_flavor(flavor_id)
246
        password = util.random_password()
247

  
248
        count = VirtualMachine.objects.filter(userid=request.user_uniq,
249
                                              deleted=False).count()
250

  
251
        # get user limit
252
        vms_limit_for_user = \
253
            settings.VMS_USER_QUOTA.get(request.user_uniq,
254
                    settings.MAX_VMS_PER_USER)
255

  
256
        if count >= vms_limit_for_user:
257
            raise faults.OverLimit("Server count limit exceeded for your account.")
258

  
259
        backend_allocator = BackendAllocator()
260
        backend = backend_allocator.allocate(flavor)
261

  
262
        if backend is None:
263
            log.error("No available backends for VM with flavor %s", flavor)
264
            raise Exception("No available backends")
265
    except:
262 266
        transaction.rollback()
263
        log.error("No available backends for VM with flavor %s", flavor)
264
        raise Exception("No available backends")
265
    transaction.commit()
266

  
267
    if settings.PUBLIC_ROUTED_USE_POOL:
268
        (network, address) = util.allocate_public_address(backend)
269
        if address is None:
270
            transaction.rollback()
271
            log.error("Public networks of backend %s are full", backend)
272
            raise faults.OverLimit("Can not allocate IP for new machine."
273
                                   " Public networks are full.")
274
        transaction.commit()
275
        nic = {'ip': address, 'network': network.backend_id}
267
        raise
276 268
    else:
277
        network = choice(list(util.backend_public_networks(backend)))
278
        nic = {'ip': 'pool', 'network': network.backend_id}
279

  
280
    # We must save the VM instance now, so that it gets a valid
281
    # vm.backend_vm_id.
282
    vm = VirtualMachine.objects.create(
283
        name=name,
284
        backend=backend,
285
        userid=request.user_uniq,
286
        imageid=image_id,
287
        flavor=flavor)
269
        transaction.commit()
288 270

  
289 271
    try:
290
        jobID = create_instance(vm, nic, flavor, image, password, personality)
291
    except GanetiApiError:
292
        vm.delete()
272
        if settings.PUBLIC_ROUTED_USE_POOL:
273
            (network, address) = util.allocate_public_address(backend)
274
            if address is None:
275
                log.error("Public networks of backend %s are full", backend)
276
                raise faults.OverLimit("Can not allocate IP for new machine."
277
                                       " Public networks are full.")
278
            nic = {'ip': address, 'network': network.backend_id}
279
        else:
280
            network = choice(list(util.backend_public_networks(backend)))
281
            nic = {'ip': 'pool', 'network': network.backend_id}
282
    except:
283
        transaction.rollback()
293 284
        raise
285
    else:
286
        transaction.commit()
294 287

  
295
    log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s",
296
            request.user_uniq, vm, nic, backend, str(jobID))
288
    try:
289
        # We must save the VM instance now, so that it gets a valid
290
        # vm.backend_vm_id.
291
        vm = VirtualMachine.objects.create(
292
            name=name,
293
            backend=backend,
294
            userid=request.user_uniq,
295
            imageid=image_id,
296
            flavor=flavor)
297 297

  
298
    vm.backendjobid = jobID
299
    vm.save()
298
        try:
299
            jobID = create_instance(vm, nic, flavor, image, password, personality)
300
        except GanetiApiError:
301
            vm.delete()
302
            raise
300 303

  
301
    for key, val in metadata.items():
302
        VirtualMachineMetadata.objects.create(
303
            meta_key=key,
304
            meta_value=val,
305
            vm=vm)
304
        log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s",
305
                request.user_uniq, vm, nic, backend, str(jobID))
306 306

  
307
    server = vm_to_dict(vm, detail=True)
308
    server['status'] = 'BUILD'
309
    server['adminPass'] = password
307
        vm.backendjobid = jobID
308
        vm.save()
309

  
310
        for key, val in metadata.items():
311
            VirtualMachineMetadata.objects.create(
312
                meta_key=key,
313
                meta_value=val,
314
                vm=vm)
310 315

  
311
    respsone = render_server(request, server, status=202)
312
    transaction.commit()
316
        server = vm_to_dict(vm, detail=True)
317
        server['status'] = 'BUILD'
318
        server['adminPass'] = password
319

  
320
        respsone = render_server(request, server, status=202)
321
    except:
322
        transaction.rollback()
323
        raise
324
    else:
325
        transaction.commit()
313 326

  
314 327
    return respsone
315 328

  

Also available in: Unified diff