Revision fde2c1f7 snf-cyclades-app/synnefo/api/floating_ips.py

b/snf-cyclades-app/synnefo/api/floating_ips.py
40 40
from snf_django.lib.api import faults, utils
41 41
from synnefo.api import util
42 42
from synnefo import quotas
43
from synnefo.db.models import Network, IPAddress, NetworkInterface
43
from synnefo.db.models import Network, IPAddress
44
from synnefo.db import pools
44 45

  
45 46

  
46 47
from logging import getLogger
......
77 78

  
78 79

  
79 80
def ip_to_dict(floating_ip):
80
    machine_id = floating_ip.machine_id
81
    machine_id = None
82
    if floating_ip.nic is not None:
83
        machine_id = floating_ip.nic.machine_id
81 84
    return {"fixed_ip": None,
82 85
            "id": str(floating_ip.id),
83 86
            "instance_id": str(machine_id) if machine_id else None,
84
            "ip": floating_ip.ipv4,
87
            "ip": floating_ip.address,
85 88
            "pool": str(floating_ip.network_id)}
86 89

  
87 90

  
......
92 95
    log.debug("list_floating_ips")
93 96

  
94 97
    userid = request.user_uniq
95
    floating_ips = IPAddress.objects.filter(userid=userid).order_by("id")
98
    floating_ips = IPAddress.objects.filter(userid=userid, deleted=False,
99
                                            floating_ip=True).order_by("id")\
100
                                    .select_related("nic")
96 101
    floating_ips = utils.filter_modified_since(request, objects=floating_ips)
97 102

  
98 103
    floating_ips = map(ip_to_dict, floating_ips)
......
108 113
def get_floating_ip(request, floating_ip_id):
109 114
    """Return information for a floating IP."""
110 115
    userid = request.user_uniq
111
    try:
112
        floating_ip = IPAddress.objects.get(id=floating_ip_id,
113
                                             deleted=False,
114
                                             userid=userid)
115
    except IPAddress.DoesNotExist:
116
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
117
                                  floating_ip_id)
116
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id)
118 117
    request.serialization = "json"
119 118
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
120 119
    return HttpResponse(data, status=200)
......
122 121

  
123 122
@api.api_method(http_method='POST', user_required=True, logger=log,
124 123
                serializations=["json"])
125
@transaction.commit_manually
124
@transaction.commit_on_success
126 125
def allocate_floating_ip(request):
127 126
    """Allocate a floating IP."""
128 127
    req = utils.get_request_dict(request)
......
131 130
    userid = request.user_uniq
132 131
    pool = req.get("pool", None)
133 132
    address = req.get("address", None)
134
    machine = None
135
    net_objects = Network.objects.select_for_update()\
136
                                 .filter(public=True, floating_ip_pool=True,
137
                                         deleted=False)
138
    try:
139
        if pool is None:
140
            # User did not specified a pool. Choose a random public IP
141
            network, address = util.get_free_ip(net_objects)
142
        else:
143
            try:
144
                network_id = int(pool)
145
            except ValueError:
146
                raise faults.BadRequest("Invalid pool ID.")
147
            network = next((n for n in net_objects if n.id == network_id),
148
                           None)
149
            if network is None:
150
                raise faults.ItemNotFound("Pool '%s' does not exist." % pool)
151
            if address is None:
152
                # User did not specified an IP address. Choose a random one
153
                # Gets X-Lock on IP pool
154
                address = util.get_network_free_address(network)
155
            else:
156
                # User specified an IP address. Check that it is not a used
157
                # floating IP
158
                if IPAddress.objects.filter(network=network,
159
                                             deleted=False,
160
                                             ipv4=address).exists():
161
                    msg = "Floating IP '%s' is reserved" % address
162
                    raise faults.Conflict(msg)
163
                pool = network.get_pool()  # Gets X-Lock
164
                # Check address belongs to pool
165
                if not pool.contains(address):
166
                    raise faults.BadRequest("Invalid address")
167
                if pool.is_available(address):
168
                    pool.reserve(address)
169
                    pool.save()
170
                # If address is not available, check that it belongs to the
171
                # same user
172
                else:
173
                    try:
174
                        nic = network.nics.get(ipv4=address,
175
                                               machine__userid=userid)
176
                        nic.ip_type = "FLOATING"
177
                        nic.save()
178
                    except NetworkInterface.DoesNotExist:
179
                        msg = "Address '%s' is already in use" % address
180
                        raise faults.Conflict(msg)
181
        floating_ip = IPAddress.objects.create(ipv4=address, network=network,
182
                                                userid=userid, machine=machine)
183
        quotas.issue_and_accept_commission(floating_ip)
184
    except:
185
        transaction.rollback()
186
        raise
187
    else:
188
        transaction.commit()
189 133

  
190
    log.info("User '%s' allocated floating IP '%s", userid, floating_ip)
134
    if pool is None:
135
        # User did not specified a pool. Choose a random public IP
136
        try:
137
            floating_ip = util.allocate_public_ip(userid=userid,
138
                                                  floating_ip=True)
139
        except pools.EmptyPool:
140
            raise faults.Conflict("No more IP addresses available.")
141
    else:
142
        try:
143
            network_id = int(pool)
144
        except ValueError:
145
            raise faults.BadRequest("Invalid pool ID.")
146
        network = util.get_network(network_id, userid, for_update=True,
147
                                   non_deleted=True)
148
        if not network.floating_ip_pool:
149
            # Check that it is a floating IP pool
150
            raise faults.ItemNotFound("Floating IP pool %s does not exist." %
151
                                      network_id)
152
        floating_ip = util.allocate_ip(network, userid, address=address,
153
                                       floating_ip=True)
154

  
155
    quotas.issue_and_accept_commission(floating_ip)
156
    transaction.commit()
157

  
158
    log.info("User '%s' allocated floating IP '%s'", userid, floating_ip)
191 159

  
192 160
    request.serialization = "json"
193 161
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
......
201 169
    """Release a floating IP."""
202 170
    userid = request.user_uniq
203 171
    log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
204
    try:
205
        floating_ip = IPAddress.objects.select_for_update()\
206
                                        .get(id=floating_ip_id,
207
                                             deleted=False,
208
                                             userid=userid)
209
    except IPAddress.DoesNotExist:
210
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
211
                                  floating_ip_id)
212

  
213
    # Since we have got an exlusively lock in floating IP, and since
214
    # to remove a floating IP you need the same lock, the in_use() query
215
    # is safe
216
    if floating_ip.in_use():
217
        msg = "Floating IP '%s' is used" % floating_ip.id
218
        raise faults.Conflict(message=msg)
219

  
220
    try:
221
        floating_ip.network.release_address(floating_ip.ipv4)
222
        floating_ip.deleted = True
223
        quotas.issue_and_accept_commission(floating_ip, delete=True)
224
    except:
225
        transaction.rollback()
226
        raise
227
    else:
228
        floating_ip.delete()
229
        transaction.commit()
172

  
173
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
174
                                             for_update=True)
175
    if floating_ip.nic:
176
        # This is safe, you also need for_update to attach floating IP to
177
        # instance.
178
        msg = "Floating IP '%s' is attached to instance." % floating_ip.id
179
        raise faults.Conflict(msg)
180

  
181
    # Return the address of the floating IP back to pool
182
    floating_ip.release_address()
183
    # And mark the floating IP as deleted
184
    floating_ip.deleted = True
185
    floating_ip.save()
186
    # Release quota for floating IP
187
    quotas.issue_and_accept_commission(floating_ip, delete=True)
188
    transaction.commit()
189
    # Delete the floating IP from DB
190
    floating_ip.delete()
230 191

  
231 192
    log.info("User '%s' released IP '%s", userid, floating_ip)
232 193

  
233 194
    return HttpResponse(status=204)
234 195

  
235 196

  
236
def network_to_pool(network):
237
    pool = network.get_pool(locked=False)
238
    return {"name": str(network.id),
239
            "size": pool.pool_size,
240
            "free": pool.count_available()}
241

  
242

  
197
# Floating IP pools
243 198
@api.api_method(http_method='GET', user_required=True, logger=log,
244 199
                serializations=["json"])
245 200
def list_floating_ip_pools(request):
246
    networks = Network.objects.filter(public=True, floating_ip_pool=True)
201
    networks = Network.objects.filter(public=True, floating_ip_pool=True,
202
                                      deleted=False)
247 203
    networks = utils.filter_modified_since(request, objects=networks)
248
    pools = map(network_to_pool, networks)
204
    pools = map(network_to_floating_ip_pool, networks)
249 205
    request.serialization = "json"
250 206
    data = json.dumps({"floating_ip_pools": pools})
251 207
    request.serialization = "json"
252 208
    return HttpResponse(data, status=200)
209

  
210

  
211
def network_to_floating_ip_pool(network):
212
    """Convert a 'Network' object to a floating IP pool dict."""
213
    total, free = network.ip_count()
214
    return {"name": str(network.id),
215
            "size": total,
216
            "free": free}

Also available in: Unified diff