Revision dca7553e

b/snf-cyclades-app/synnefo/api/servers.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from base64 import b64decode
35

  
36 34
from django.conf import settings
37 35
from django.conf.urls.defaults import patterns
38 36
from django.db import transaction
......
48 46
from synnefo.logic.utils import get_rsapi_state
49 47
from synnefo.logic.rapi import GanetiApiError
50 48
from synnefo.logic.backend_allocator import BackendAllocator
51
from random import choice
52 49

  
53 50

  
54 51
from logging import getLogger
......
258 255
    try:
259 256
        req = util.get_request_dict(request)
260 257
        log.info('create_server %s', req)
258
        user_id = request.user_uniq
261 259

  
262 260
        try:
263 261
            server = req['server']
......
274 272
        if len(personality) > settings.MAX_PERSONALITY:
275 273
            raise faults.OverLimit("Maximum number of personalities exceeded")
276 274

  
277
        for p in personality:
278
            # Verify that personalities are well-formed
279
            try:
280
                assert isinstance(p, dict)
281
                keys = set(p.keys())
282
                allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
283
                assert keys.issubset(allowed)
284
                contents = p['contents']
285
                if len(contents) > settings.MAX_PERSONALITY_SIZE:
286
                    # No need to decode if contents already exceed limit
287
                    raise faults.OverLimit("Maximum size of personality exceeded")
288
                if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
289
                    raise faults.OverLimit("Maximum size of personality exceeded")
290
            except AssertionError:
291
                raise faults.BadRequest("Malformed personality in request")
292

  
293
        image = {}
294
        img = util.get_image(image_id, request.user_uniq)
295
        properties = img.get('properties', {})
296
        image['backend_id'] = img['location']
297
        image['format'] = img['disk_format']
298
        image['metadata'] = dict((key.upper(), val) \
299
                                 for key, val in properties.items())
300

  
301
        # Ensure that request if for active flavor
302
        flavor = util.get_flavor(flavor_id, include_deleted=False)
275
        util.verify_personality(personality)
276
        image = util.get_image_dict(image_id, user_id)
277
        flavor = util.get_flavor(flavor_id)
303 278
        password = util.random_password()
304 279

  
305
        count = VirtualMachine.objects.filter(userid=request.user_uniq,
280
        count = VirtualMachine.objects.filter(userid=user_id,
306 281
                                              deleted=False).count()
307 282

  
308 283
        # get user limit
309 284
        vms_limit_for_user = \
310
            settings.VMS_USER_QUOTA.get(request.user_uniq,
285
            settings.VMS_USER_QUOTA.get(user_id,
311 286
                    settings.MAX_VMS_PER_USER)
312 287

  
313 288
        if count >= vms_limit_for_user:
......
326 301
        transaction.commit()
327 302

  
328 303
    try:
329
        if settings.PUBLIC_USE_POOL:
330
            (network, address) = util.allocate_public_address(backend)
331
            if address is None:
332
                log.error("Public networks of backend %s are full", backend)
333
                msg = "Failed to allocate public IP for new VM"
334
                raise faults.ServiceUnavailable(msg)
335
            nic = {'ip': address, 'network': network.backend_id}
336
        else:
337
            network = choice(list(util.backend_public_networks(backend)))
338
            nic = {'ip': 'pool', 'network': network.backend_id}
304
        (network, address) = util.get_public_ip(backend)
305
        nic = {'ip': address, 'network': network.backend_id}
339 306
    except:
340 307
        transaction.rollback()
341
        raise
342 308
    else:
343 309
        transaction.commit()
344 310

  
......
348 314
        vm = VirtualMachine.objects.create(
349 315
            name=name,
350 316
            backend=backend,
351
            userid=request.user_uniq,
317
            userid=user_id,
352 318
            imageid=image_id,
353 319
            flavor=flavor,
354 320
            action="CREATE")
355 321

  
356 322
        try:
357
            jobID = create_instance(vm, nic, flavor, image, password, personality)
323
            jobID = create_instance(vm, nic, flavor, image, password,
324
                                    personality)
358 325
        except GanetiApiError:
359 326
            vm.delete()
360 327
            raise
361 328

  
362 329
        log.info("User %s created VM %s, NIC %s, Backend %s, JobID %s",
363
                request.user_uniq, vm, nic, backend, str(jobID))
330
                user_id, vm, nic, backend, str(jobID))
364 331

  
365 332
        vm.backendjobid = jobID
366 333
        vm.save()
b/snf-cyclades-app/synnefo/api/util.py
33 33

  
34 34
import datetime
35 35

  
36
from base64 import b64encode
36
from base64 import b64encode, b64decode
37 37
from datetime import timedelta, tzinfo
38 38
from functools import wraps
39 39
from hashlib import sha256
......
57 57

  
58 58
from synnefo.api.faults import (Fault, BadRequest, BuildInProgress,
59 59
                                ItemNotFound, ServiceUnavailable, Unauthorized,
60
                                BadMediaType, Forbidden)
60
                                BadMediaType, Forbidden, OverLimit)
61 61
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata,
62 62
                               Network, BackendNetwork, NetworkInterface,
63 63
                               BridgePoolTable, MacPrefixPoolTable)
......
200 200
        backend.close()
201 201

  
202 202

  
203
def get_image_dict(image_id, user_id):
204
    image = {}
205
    img = get_image(image_id, user_id)
206
    properties = img.get('properties', {})
207
    image['backend_id'] = img['location']
208
    image['format'] = img['disk_format']
209
    image['metadata'] = dict((key.upper(), val) \
210
                             for key, val in properties.items())
211
    return image
212

  
213

  
203 214
def get_flavor(flavor_id, include_deleted=False):
204 215
    """Return a Flavor instance or raise ItemNotFound."""
205 216

  
......
242 253
    return (None, None)
243 254

  
244 255

  
256
def get_public_ip(backend):
257
    """Reserve an IP from a public network.
258

  
259
    This method should run inside a transaction.
260

  
261
    """
262
    address = None
263
    if settings.PUBLIC_ROUTED_USE_POOL:
264
        (network, address) = allocate_public_address(backend)
265
    else:
266
        for net in list(backend_public_networks(backend)):
267
            pool = net.get_pool()
268
            if not pool.empty():
269
                address = 'pool'
270
                network = net
271
                break
272
    if address is None:
273
        log.error("Public networks of backend %s are full", backend)
274
        raise OverLimit("Can not allocate IP for new machine."
275
                        " Public networks are full.")
276
    return (network, address)
277

  
278

  
245 279
def backend_public_networks(backend):
246 280
    """Return available public networks of the backend.
247 281

  
......
445 479
        raise BadRequest('Unknown network type')
446 480

  
447 481
    return link, mac_prefix
482

  
483

  
484
def verify_personality(personality):
485
    for p in personality:
486
        # Verify that personalities are well-formed
487
        try:
488
            assert isinstance(p, dict)
489
            keys = set(p.keys())
490
            allowed = set(['contents', 'group', 'mode', 'owner', 'path'])
491
            assert keys.issubset(allowed)
492
            contents = p['contents']
493
            if len(contents) > settings.MAX_PERSONALITY_SIZE:
494
                # No need to decode if contents already exceed limit
495
                raise OverLimit("Maximum size of personality exceeded")
496
            if len(b64decode(contents)) > settings.MAX_PERSONALITY_SIZE:
497
                raise OverLimit("Maximum size of personality exceeded")
498
        except AssertionError:
499
            raise BadRequest("Malformed personality in request")

Also available in: Unified diff