Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / ips.py @ ee688a71

History | View | Annotate | Download (6.8 kB)

1
import logging
2

    
3
from snf_django.lib.api import faults
4
from django.db import transaction
5
from synnefo import quotas
6
from synnefo.db import pools
7
from synnefo.db.models import (IPPoolTable, IPAddress)
8
log = logging.getLogger(__name__)
9

    
10

    
11
def allocate_ip_from_pools(pool_rows, userid, address=None, floating_ip=False):
12
    """Try to allocate a value from a number of pools.
13

14
    This function takes as argument a number of PoolTable objects and tries to
15
    allocate a value from them. If all pools are empty EmptyPool is raised.
16
    If an address is specified and does not belong to any of the pools,
17
    InvalidValue is raised.
18

19
    """
20
    for pool_row in pool_rows:
21
        pool = pool_row.pool
22
        try:
23
            value = pool.get(value=address)
24
            pool.save()
25
            subnet = pool_row.subnet
26
            ipaddress = IPAddress.objects.create(subnet=subnet,
27
                                                 network=subnet.network,
28
                                                 userid=userid,
29
                                                 address=value,
30
                                                 floating_ip=floating_ip)
31
            return ipaddress
32
        except pools.EmptyPool:
33
            pass
34
        except pools.InvalidValue:
35
            pass
36
    if address is None:
37
        raise pools.EmptyPool("No more IP addresses available on pools %s" %
38
                              pool_rows)
39
    else:
40
        raise pools.InvalidValue("Address %s does not belong to pools %s" %
41
                                 (address, pool_rows))
42

    
43

    
44
def allocate_ip(network, userid, address=None, floating_ip=False):
45
    """Try to allocate an IP from networks IP pools."""
46
    if network.action == "DESTROY":
47
        raise faults.Conflict("Can not allocate IP. Network %s is being"
48
                              " deleted" % network.id)
49
    ip_pools = IPPoolTable.objects.select_for_update()\
50
        .filter(subnet__network=network)
51
    try:
52
        return allocate_ip_from_pools(ip_pools, userid, address=address,
53
                                      floating_ip=floating_ip)
54
    except pools.EmptyPool:
55
        raise faults.Conflict("No more IP addresses available on network %s"
56
                              % network.id)
57
    except pools.ValueNotAvailable:
58
        raise faults.Conflict("IP address %s is already used." % address)
59
    except pools.InvalidValue:
60
        raise faults.BadRequest("Address %s does not belong to network %s" %
61
                                (address, network.id))
62

    
63

    
64
def allocate_public_ip(userid, floating_ip=False, backend=None, networks=None):
65
    """Try to allocate a public or floating IP address.
66

67
    Try to allocate a a public IPv4 address from one of the available networks.
68
    If 'floating_ip' is set, only networks which are floating IP pools will be
69
    used and the IPAddress that will be created will be marked as a floating
70
    IP. If 'backend' is set, only the networks that exist in this backend will
71
    be used.
72

73
    """
74

    
75
    ip_pool_rows = IPPoolTable.objects.select_for_update()\
76
        .prefetch_related("subnet__network")\
77
        .filter(subnet__deleted=False)\
78
        .filter(subnet__network__deleted=False)\
79
        .filter(subnet__network__public=True)\
80
        .filter(subnet__network__drained=False)
81
    if networks is not None:
82
        ip_pool_rows = ip_pool_rows.filter(subnet__network__in=networks)
83
    if floating_ip:
84
        ip_pool_rows = ip_pool_rows\
85
            .filter(subnet__network__floating_ip_pool=True)
86
    if backend is not None:
87
        ip_pool_rows = ip_pool_rows\
88
            .filter(subnet__network__backend_networks__backend=backend)
89

    
90
    try:
91
        return allocate_ip_from_pools(ip_pool_rows, userid,
92
                                      floating_ip=floating_ip)
93
    except pools.EmptyPool:
94
        ip_type = "floating" if floating_ip else "public"
95
        log_msg = "Failed to allocate a %s IP. Reason:" % ip_type
96
        if ip_pool_rows:
97
            log_msg += " No network exists."
98
        else:
99
            log_msg += " All network are full."
100
        if backend is not None:
101
            log_msg += " Backend: %s" % backend
102
        log.error(log_msg)
103
        exception_msg = "Can not allocate a %s IP address." % ip_type
104
        raise faults.Conflict(exception_msg)
105

    
106

    
107
@transaction.commit_on_success
108
def create_floating_ip(userid, network=None, address=None):
109
    if network is None:
110
        floating_ip = allocate_public_ip(userid, floating_ip=True)
111
    else:
112
        if not network.floating_ip_pool:
113
            msg = ("Can not allocate floating IP. Network %s is"
114
                   " not a floating IP pool.")
115
            raise faults.Conflict(msg % network.id)
116
        if network.action == "DESTROY":
117
            msg = "Can not allocate floating IP. Network %s is being deleted."
118
            raise faults.Conflict(msg % network.id)
119

    
120
        # Allocate the floating IP
121
        floating_ip = allocate_ip(network, userid, address=address,
122
                                  floating_ip=True)
123

    
124
    # Issue commission (quotas)
125
    quotas.issue_and_accept_commission(floating_ip)
126
    transaction.commit()
127

    
128
    log.info("Created floating IP '%s' for user IP '%s'", floating_ip, userid)
129

    
130
    return floating_ip
131

    
132

    
133
def get_free_floating_ip(userid, network=None):
134
    """Get one of the free available floating IPs of the user.
135

136
    Get one of the users floating IPs that is not connected to any port
137
    or server. If network is specified, the floating IP must be from
138
    that network.
139

140
    """
141
    floating_ips = IPAddress.objects\
142
                            .filter(userid=userid, deleted=False, nic=None)
143
    if network is not None:
144
        floating_ips = floating_ips.filter(network=network)
145

    
146
    for floating_ip in floating_ips:
147
        floating_ip = IPAddress.objects.select_for_update()\
148
                                       .get(id=floating_ip.id)
149
        if floating_ip.nic is None:
150
            return floating_ip
151

    
152
    msg = "Cannot allocate a floating IP for connecting new server to"
153
    if network is not None:
154
        msg += " network '%s'." % network.id
155
    else:
156
        msg += " a public network."
157
    msg += " Please create more floating IPs."
158
    raise faults.Conflict(msg)
159

    
160

    
161
@transaction.commit_on_success
162
def delete_floating_ip(floating_ip):
163
    if floating_ip.nic:
164
        # This is safe, you also need for_update to attach floating IP to
165
        # instance.
166
        msg = "Floating IP '%s' is attached to instance." % floating_ip.id
167
        raise faults.Conflict(msg)
168

    
169
    # Return the address of the floating IP back to pool
170
    floating_ip.release_address()
171
    # And mark the floating IP as deleted
172
    floating_ip.deleted = True
173
    floating_ip.save()
174
    # Release quota for floating IP
175
    quotas.issue_and_accept_commission(floating_ip, delete=True)
176
    transaction.commit()
177
    # Delete the floating IP from DB
178
    log.info("Deleted floating IP '%s' of user '%s", floating_ip,
179
             floating_ip.userid)
180
    floating_ip.delete()