Revision 710b1c43 snf-cyclades-app/synnefo/api/util.py

b/snf-cyclades-app/synnefo/api/util.py
47 47

  
48 48
from snf_django.lib.api import faults
49 49
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata,
50
                               Network, BackendNetwork, NetworkInterface,
51
                               BridgePoolTable, MacPrefixPoolTable, Backend,
52
                               IPAddress)
53
from synnefo.db.pools import EmptyPool
50
                               Network, NetworkInterface, BridgePoolTable,
51
                               MacPrefixPoolTable, IPAddress, IPPoolTable)
52
from synnefo.db import pools
54 53

  
55 54
from synnefo.plankton.utils import image_backend
56 55

  
......
223 222
            raise faults.BadRequest("Network has been deleted.")
224 223
        return network
225 224
    except (ValueError, Network.DoesNotExist):
226
        raise faults.ItemNotFound('Network not found.')
225
        raise faults.ItemNotFound('Network %s not found.' % network_id)
227 226

  
228 227

  
229 228
def get_port(port_id, user_id, for_update=False):
......
245 244
        raise faults.ItemNotFound('Port not found.')
246 245

  
247 246

  
248
def get_floating_ip(user_id, ipv4, for_update=False):
247
def get_floating_ip_by_address(userid, address, for_update=False):
249 248
    try:
250 249
        objects = IPAddress.objects
251 250
        if for_update:
252 251
            objects = objects.select_for_update()
253
        return objects.get(userid=user_id, ipv4=ipv4, deleted=False)
252
        return objects.get(userid=userid, floating_ip=True,
253
                           address=address, deleted=False)
254 254
    except IPAddress.DoesNotExist:
255 255
        raise faults.ItemNotFound("Floating IP does not exist.")
256 256

  
257 257

  
258
def allocate_public_address(backend, userid):
259
    """Get a public IP for any available network of a backend."""
260
    # Guarantee exclusive access to backend, because accessing the IP pools of
261
    # the backend networks may result in a deadlock with backend allocator
262
    # which also checks that backend networks have a free IP.
263
    backend = Backend.objects.select_for_update().get(id=backend.id)
264
    public_networks = backend_public_networks(backend)
265
    return get_free_ip(public_networks, userid)
258
def get_floating_ip_by_id(userid, floating_ip_id, for_update=False):
259
    try:
260
        objects = IPAddress.objects
261
        if for_update:
262
            objects = objects.select_for_update()
263
        return objects.get(id=floating_ip_id, floating_ip=True, userid=userid,
264
                           deleted=False)
265
    except IPAddress.DoesNotExist:
266
        raise faults.ItemNotFound("Floating IP %s does not exist." %
267
                                  floating_ip_id)
266 268

  
267 269

  
268
def backend_public_networks(backend):
269
    """Return available public networks of the backend.
270
def allocate_ip_from_pools(pool_rows, userid, address=None, floating_ip=False):
271
    """Try to allocate a value from a number of pools.
270 272

  
271
    Iterator for non-deleted public networks that are available
272
    to the specified backend.
273
    This function takes as argument a number of PoolTable objects and tries to
274
    allocate a value from them. If all pools are empty EmptyPool is raised.
273 275

  
274 276
    """
275
    bnets = BackendNetwork.objects.filter(backend=backend,
276
                                          network__public=True,
277
                                          network__deleted=False,
278
                                          network__floating_ip_pool=False,
279
                                          network__drained=False)
280
    return [b.network for b in bnets]
281

  
282

  
283
def get_free_ip(networks, userid):
284
    for network in networks:
277
    for pool_row in pool_rows:
278
        pool = pool_row.pool
285 279
        try:
286
            return network.allocate_address(userid=userid)
287
        except faults.OverLimit:
280
            value = pool.get(value=address)
281
            pool.save()
282
            subnet = pool_row.subnet
283
            ipaddress = IPAddress.objects.create(subnet=subnet,
284
                                                 network=subnet.network,
285
                                                 userid=userid,
286
                                                 address=value,
287
                                                 floating_ip=floating_ip)
288
            return ipaddress
289
        except pools.EmptyPool:
288 290
            pass
289
    msg = "Can not allocate public IP. Public networks are full."
290
    log.error(msg)
291
    raise faults.OverLimit(msg)
291
    raise pools.EmptyPool("No more IP addresses available on pools %s" %
292
                          pool_rows)
293

  
294

  
295
def allocate_ip(network, userid, address=None, floating_ip=False):
296
    """Try to allocate an IP from networks IP pools."""
297
    ip_pools = IPPoolTable.objects.select_for_update()\
298
        .filter(subnet__network=network)
299
    try:
300
        return allocate_ip_from_pools(ip_pools, userid, address=address,
301
                                      floating_ip=floating_ip)
302
    except pools.EmptyPool:
303
        raise faults.Conflict("No more IP addresses available on network %s"
304
                              % network.id)
305
    except pools.ValueNotAvailable:
306
        raise faults.Conflict("IP address %s is already used." % address)
307

  
308

  
309
def allocate_public_ip(userid, floating_ip=False, backend=None):
310
    """Try to allocate a public or floating IP address.
292 311

  
312
    Try to allocate a a public IPv4 address from one of the available networks.
313
    If 'floating_ip' is set, only networks which are floating IP pools will be
314
    used and the IPAddress that will be created will be marked as a floating
315
    IP. If 'backend' is set, only the networks that exist in this backend will
316
    be used.
317

  
318
    """
293 319

  
294
def get_network_free_address(network, userid):
295
    """Reserve an IP address from the IP Pool of the network."""
320
    ip_pool_rows = IPPoolTable.objects.select_for_update()\
321
        .prefetch_related("subnet__network")\
322
        .filter(subnet__deleted=False)\
323
        .filter(subnet__network__public=True)\
324
        .filter(subnet__network__drained=False)
325
    if floating_ip:
326
        ip_pool_rows = ip_pool_rows\
327
            .filter(subnet__network__floating_ip_pool=True)
328
    if backend is not None:
329
        ip_pool_rows = ip_pool_rows\
330
            .filter(subnet__network__backend_networks__backend=backend)
296 331

  
297 332
    try:
298
        return network.allocate_address(userid=userid)
299
    except EmptyPool:
300
        raise faults.OverLimit("Network %s is full." % network.backend_id)
333
        return allocate_ip_from_pools(ip_pool_rows, userid,
334
                                      floating_ip=floating_ip)
335
    except pools.EmptyPool:
336
        ip_type = "floating" if floating_ip else "public"
337
        log_msg = "Failed to allocate a %s IP. Reason:" % ip_type
338
        if ip_pool_rows:
339
            log_msg += " No network exists."
340
        else:
341
            log_msg += " All network are full."
342
        if backend is not None:
343
            log_msg += " Backend: %s" % backend
344
        log.error(log_msg)
345
        exception_msg = "Can not allocate a %s IP address." % ip_type
346
        raise faults.ServiceUnavailable(exception_msg)
347

  
348

  
349
def backend_has_free_public_ip(backend):
350
    """Check if a backend has a free public IPv4 address."""
351
    ip_pool_rows = IPPoolTable.objects.select_for_update()\
352
        .filter(subnet__network__public=True)\
353
        .filter(subnet__network__drained=False)\
354
        .filter(subnet__deleted=False)\
355
        .filter(subnet__network__backend_networks__backend=backend)
356
    for pool_row in ip_pool_rows:
357
        pool = pool_row.pool
358
        if pool.empty():
359
            continue
360
        else:
361
            return True
362

  
363

  
364
def backend_public_networks(backend):
365
    return Network.objects.filter(deleted=False, public=True,
366
                                  backend_networks__backend=backend)
301 367

  
302 368

  
303 369
def get_vm_nic(vm, nic_id):

Also available in: Unified diff