Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / floating_ips.py @ ab6d1a2f

History | View | Annotate | Download (9.5 kB)

1 59183afc Christos Stavrakakis
# Copyright 2013 GRNET S.A. All rights reserved.
2 59183afc Christos Stavrakakis
#
3 59183afc Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 59183afc Christos Stavrakakis
# without modification, are permitted provided that the following
5 59183afc Christos Stavrakakis
# conditions are met:
6 59183afc Christos Stavrakakis
#
7 59183afc Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 59183afc Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 59183afc Christos Stavrakakis
#      disclaimer.
10 59183afc Christos Stavrakakis
#
11 59183afc Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 59183afc Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 59183afc Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 59183afc Christos Stavrakakis
#      provided with the distribution.
15 59183afc Christos Stavrakakis
#
16 59183afc Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 59183afc Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 59183afc Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 59183afc Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 59183afc Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 59183afc Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 59183afc Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 59183afc Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 59183afc Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 59183afc Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 59183afc Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 59183afc Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 59183afc Christos Stavrakakis
#
29 59183afc Christos Stavrakakis
# The views and conclusions contained in the software and
30 59183afc Christos Stavrakakis
# documentation are those of the authors and should not be
31 59183afc Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 59183afc Christos Stavrakakis
# or implied, of GRNET S.A.
33 59183afc Christos Stavrakakis
34 59183afc Christos Stavrakakis
from django.conf.urls.defaults import patterns
35 59183afc Christos Stavrakakis
from django.db import transaction
36 59183afc Christos Stavrakakis
from django.http import HttpResponse
37 59183afc Christos Stavrakakis
from django.utils import simplejson as json
38 59183afc Christos Stavrakakis
39 59183afc Christos Stavrakakis
from snf_django.lib import api
40 59183afc Christos Stavrakakis
from snf_django.lib.api import faults, utils
41 59183afc Christos Stavrakakis
from synnefo.api import util
42 59183afc Christos Stavrakakis
from synnefo import quotas
43 326c3ec8 Christos Stavrakakis
from synnefo.db.models import Network, IPAddress, NetworkInterface
44 59183afc Christos Stavrakakis
45 59183afc Christos Stavrakakis
46 59183afc Christos Stavrakakis
from logging import getLogger
47 59183afc Christos Stavrakakis
log = getLogger(__name__)
48 59183afc Christos Stavrakakis
49 7d368e73 Christos Stavrakakis
ips_urlpatterns = patterns(
50 59183afc Christos Stavrakakis
    'synnefo.api.floating_ips',
51 59183afc Christos Stavrakakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
52 59183afc Christos Stavrakakis
    (r'^/(\w+)(?:.json|.xml)?$', 'floating_ip_demux'),
53 59183afc Christos Stavrakakis
)
54 59183afc Christos Stavrakakis
55 7d368e73 Christos Stavrakakis
pools_urlpatterns = patterns(
56 7d368e73 Christos Stavrakakis
    "synnefo.api.floating_ips",
57 7d368e73 Christos Stavrakakis
    (r'^(?:/|.json|.xml)?$', 'list_floating_ip_pools'),
58 7d368e73 Christos Stavrakakis
)
59 7d368e73 Christos Stavrakakis
60 59183afc Christos Stavrakakis
61 59183afc Christos Stavrakakis
def demux(request):
62 59183afc Christos Stavrakakis
    if request.method == 'GET':
63 59183afc Christos Stavrakakis
        return list_floating_ips(request)
64 59183afc Christos Stavrakakis
    elif request.method == 'POST':
65 59183afc Christos Stavrakakis
        return allocate_floating_ip(request)
66 59183afc Christos Stavrakakis
    else:
67 20a626e7 Giorgos Korfiatis
        return api.api_method_not_allowed(request)
68 59183afc Christos Stavrakakis
69 59183afc Christos Stavrakakis
70 59183afc Christos Stavrakakis
def floating_ip_demux(request, floating_ip_id):
71 59183afc Christos Stavrakakis
    if request.method == 'GET':
72 59183afc Christos Stavrakakis
        return get_floating_ip(request, floating_ip_id)
73 59183afc Christos Stavrakakis
    elif request.method == 'DELETE':
74 59183afc Christos Stavrakakis
        return release_floating_ip(request, floating_ip_id)
75 59183afc Christos Stavrakakis
    else:
76 20a626e7 Giorgos Korfiatis
        return api.api_method_not_allowed(request)
77 59183afc Christos Stavrakakis
78 59183afc Christos Stavrakakis
79 59183afc Christos Stavrakakis
def ip_to_dict(floating_ip):
80 59183afc Christos Stavrakakis
    machine_id = floating_ip.machine_id
81 59183afc Christos Stavrakakis
    return {"fixed_ip": None,
82 59183afc Christos Stavrakakis
            "id": str(floating_ip.id),
83 59183afc Christos Stavrakakis
            "instance_id": str(machine_id) if machine_id else None,
84 59183afc Christos Stavrakakis
            "ip": floating_ip.ipv4,
85 59183afc Christos Stavrakakis
            "pool": str(floating_ip.network_id)}
86 59183afc Christos Stavrakakis
87 59183afc Christos Stavrakakis
88 1cbe2522 Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log,
89 1cbe2522 Christos Stavrakakis
                serializations=["json"])
90 59183afc Christos Stavrakakis
def list_floating_ips(request):
91 59183afc Christos Stavrakakis
    """Return user reserved floating IPs"""
92 59183afc Christos Stavrakakis
    log.debug("list_floating_ips")
93 59183afc Christos Stavrakakis
94 59183afc Christos Stavrakakis
    userid = request.user_uniq
95 326c3ec8 Christos Stavrakakis
    floating_ips = IPAddress.objects.filter(userid=userid).order_by("id")
96 b6bc4afa Christos Stavrakakis
    floating_ips = utils.filter_modified_since(request, objects=floating_ips)
97 59183afc Christos Stavrakakis
98 59183afc Christos Stavrakakis
    floating_ips = map(ip_to_dict, floating_ips)
99 59183afc Christos Stavrakakis
100 59183afc Christos Stavrakakis
    request.serialization = "json"
101 59183afc Christos Stavrakakis
    data = json.dumps({"floating_ips": floating_ips})
102 59183afc Christos Stavrakakis
103 59183afc Christos Stavrakakis
    return HttpResponse(data, status=200)
104 59183afc Christos Stavrakakis
105 59183afc Christos Stavrakakis
106 1cbe2522 Christos Stavrakakis
@api.api_method(http_method="GET", user_required=True, logger=log,
107 1cbe2522 Christos Stavrakakis
                serializations=["json"])
108 59183afc Christos Stavrakakis
def get_floating_ip(request, floating_ip_id):
109 59183afc Christos Stavrakakis
    """Return information for a floating IP."""
110 59183afc Christos Stavrakakis
    userid = request.user_uniq
111 59183afc Christos Stavrakakis
    try:
112 326c3ec8 Christos Stavrakakis
        floating_ip = IPAddress.objects.get(id=floating_ip_id,
113 59183afc Christos Stavrakakis
                                             deleted=False,
114 59183afc Christos Stavrakakis
                                             userid=userid)
115 326c3ec8 Christos Stavrakakis
    except IPAddress.DoesNotExist:
116 59183afc Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
117 59183afc Christos Stavrakakis
                                  floating_ip_id)
118 59183afc Christos Stavrakakis
    request.serialization = "json"
119 59183afc Christos Stavrakakis
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
120 59183afc Christos Stavrakakis
    return HttpResponse(data, status=200)
121 59183afc Christos Stavrakakis
122 59183afc Christos Stavrakakis
123 1cbe2522 Christos Stavrakakis
@api.api_method(http_method='POST', user_required=True, logger=log,
124 1cbe2522 Christos Stavrakakis
                serializations=["json"])
125 59183afc Christos Stavrakakis
@transaction.commit_manually
126 59183afc Christos Stavrakakis
def allocate_floating_ip(request):
127 59183afc Christos Stavrakakis
    """Allocate a floating IP."""
128 59183afc Christos Stavrakakis
    req = utils.get_request_dict(request)
129 59183afc Christos Stavrakakis
    log.info('allocate_floating_ip %s', req)
130 59183afc Christos Stavrakakis
131 59183afc Christos Stavrakakis
    userid = request.user_uniq
132 9e8be4fb Christos Stavrakakis
    pool = req.get("pool", None)
133 a3d99af0 Christos Stavrakakis
    address = req.get("address", None)
134 a3d99af0 Christos Stavrakakis
    machine = None
135 9115d567 Christos Stavrakakis
    net_objects = Network.objects.select_for_update()\
136 9115d567 Christos Stavrakakis
                                 .filter(public=True, floating_ip_pool=True,
137 9115d567 Christos Stavrakakis
                                         deleted=False)
138 59183afc Christos Stavrakakis
    try:
139 9e8be4fb Christos Stavrakakis
        if pool is None:
140 9e8be4fb Christos Stavrakakis
            # User did not specified a pool. Choose a random public IP
141 660b9f3b Christos Stavrakakis
            network, address = util.get_free_ip(net_objects)
142 a3d99af0 Christos Stavrakakis
        else:
143 9e8be4fb Christos Stavrakakis
            try:
144 0c50c760 Christos Stavrakakis
                network_id = int(pool)
145 32e4e343 Christos Stavrakakis
            except ValueError:
146 0c50c760 Christos Stavrakakis
                raise faults.BadRequest("Invalid pool ID.")
147 32e4e343 Christos Stavrakakis
            network = next((n for n in net_objects if n.id == network_id),
148 32e4e343 Christos Stavrakakis
                           None)
149 0c50c760 Christos Stavrakakis
            if network is None:
150 9e8be4fb Christos Stavrakakis
                raise faults.ItemNotFound("Pool '%s' does not exist." % pool)
151 9e8be4fb Christos Stavrakakis
            if address is None:
152 9e8be4fb Christos Stavrakakis
                # User did not specified an IP address. Choose a random one
153 9e8be4fb Christos Stavrakakis
                # Gets X-Lock on IP pool
154 9e8be4fb Christos Stavrakakis
                address = util.get_network_free_address(network)
155 9e8be4fb Christos Stavrakakis
            else:
156 9e8be4fb Christos Stavrakakis
                # User specified an IP address. Check that it is not a used
157 9e8be4fb Christos Stavrakakis
                # floating IP
158 326c3ec8 Christos Stavrakakis
                if IPAddress.objects.filter(network=network,
159 9e8be4fb Christos Stavrakakis
                                             deleted=False,
160 9e8be4fb Christos Stavrakakis
                                             ipv4=address).exists():
161 9e8be4fb Christos Stavrakakis
                    msg = "Floating IP '%s' is reserved" % address
162 a3d99af0 Christos Stavrakakis
                    raise faults.Conflict(msg)
163 9e8be4fb Christos Stavrakakis
                pool = network.get_pool()  # Gets X-Lock
164 9e8be4fb Christos Stavrakakis
                # Check address belongs to pool
165 9e8be4fb Christos Stavrakakis
                if not pool.contains(address):
166 9e8be4fb Christos Stavrakakis
                    raise faults.BadRequest("Invalid address")
167 9e8be4fb Christos Stavrakakis
                if pool.is_available(address):
168 9e8be4fb Christos Stavrakakis
                    pool.reserve(address)
169 9e8be4fb Christos Stavrakakis
                    pool.save()
170 9e8be4fb Christos Stavrakakis
                # If address is not available, check that it belongs to the
171 9e8be4fb Christos Stavrakakis
                # same user
172 443315d6 Christos Stavrakakis
                else:
173 443315d6 Christos Stavrakakis
                    try:
174 443315d6 Christos Stavrakakis
                        nic = network.nics.get(ipv4=address,
175 443315d6 Christos Stavrakakis
                                               machine__userid=userid)
176 443315d6 Christos Stavrakakis
                        nic.ip_type = "FLOATING"
177 443315d6 Christos Stavrakakis
                        nic.save()
178 443315d6 Christos Stavrakakis
                    except NetworkInterface.DoesNotExist:
179 9e8be4fb Christos Stavrakakis
                        msg = "Address '%s' is already in use" % address
180 9e8be4fb Christos Stavrakakis
                        raise faults.Conflict(msg)
181 326c3ec8 Christos Stavrakakis
        floating_ip = IPAddress.objects.create(ipv4=address, network=network,
182 a3d99af0 Christos Stavrakakis
                                                userid=userid, machine=machine)
183 59183afc Christos Stavrakakis
        quotas.issue_and_accept_commission(floating_ip)
184 59183afc Christos Stavrakakis
    except:
185 59183afc Christos Stavrakakis
        transaction.rollback()
186 59183afc Christos Stavrakakis
        raise
187 59183afc Christos Stavrakakis
    else:
188 59183afc Christos Stavrakakis
        transaction.commit()
189 59183afc Christos Stavrakakis
190 59183afc Christos Stavrakakis
    log.info("User '%s' allocated floating IP '%s", userid, floating_ip)
191 59183afc Christos Stavrakakis
192 59183afc Christos Stavrakakis
    request.serialization = "json"
193 59183afc Christos Stavrakakis
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
194 59183afc Christos Stavrakakis
    return HttpResponse(data, status=200)
195 59183afc Christos Stavrakakis
196 59183afc Christos Stavrakakis
197 1cbe2522 Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log,
198 1cbe2522 Christos Stavrakakis
                serializations=["json"])
199 59183afc Christos Stavrakakis
@transaction.commit_on_success
200 59183afc Christos Stavrakakis
def release_floating_ip(request, floating_ip_id):
201 59183afc Christos Stavrakakis
    """Release a floating IP."""
202 59183afc Christos Stavrakakis
    userid = request.user_uniq
203 59183afc Christos Stavrakakis
    log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
204 59183afc Christos Stavrakakis
    try:
205 326c3ec8 Christos Stavrakakis
        floating_ip = IPAddress.objects.select_for_update()\
206 59183afc Christos Stavrakakis
                                        .get(id=floating_ip_id,
207 59183afc Christos Stavrakakis
                                             deleted=False,
208 59183afc Christos Stavrakakis
                                             userid=userid)
209 326c3ec8 Christos Stavrakakis
    except IPAddress.DoesNotExist:
210 59183afc Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
211 59183afc Christos Stavrakakis
                                  floating_ip_id)
212 59183afc Christos Stavrakakis
213 72dea98f Christos Stavrakakis
    # Since we have got an exlusively lock in floating IP, and since
214 72dea98f Christos Stavrakakis
    # to remove a floating IP you need the same lock, the in_use() query
215 72dea98f Christos Stavrakakis
    # is safe
216 59183afc Christos Stavrakakis
    if floating_ip.in_use():
217 59183afc Christos Stavrakakis
        msg = "Floating IP '%s' is used" % floating_ip.id
218 59183afc Christos Stavrakakis
        raise faults.Conflict(message=msg)
219 59183afc Christos Stavrakakis
220 59183afc Christos Stavrakakis
    try:
221 59183afc Christos Stavrakakis
        floating_ip.network.release_address(floating_ip.ipv4)
222 59183afc Christos Stavrakakis
        floating_ip.deleted = True
223 59183afc Christos Stavrakakis
        quotas.issue_and_accept_commission(floating_ip, delete=True)
224 59183afc Christos Stavrakakis
    except:
225 59183afc Christos Stavrakakis
        transaction.rollback()
226 59183afc Christos Stavrakakis
        raise
227 59183afc Christos Stavrakakis
    else:
228 59183afc Christos Stavrakakis
        floating_ip.delete()
229 59183afc Christos Stavrakakis
        transaction.commit()
230 59183afc Christos Stavrakakis
231 59183afc Christos Stavrakakis
    log.info("User '%s' released IP '%s", userid, floating_ip)
232 59183afc Christos Stavrakakis
233 59183afc Christos Stavrakakis
    return HttpResponse(status=204)
234 7d368e73 Christos Stavrakakis
235 7d368e73 Christos Stavrakakis
236 0ace35e4 Christos Stavrakakis
def network_to_pool(network):
237 92d2d1ce Christos Stavrakakis
    pool = network.get_pool(locked=False)
238 0ace35e4 Christos Stavrakakis
    return {"name": str(network.id),
239 0ace35e4 Christos Stavrakakis
            "size": pool.pool_size,
240 0ace35e4 Christos Stavrakakis
            "free": pool.count_available()}
241 0ace35e4 Christos Stavrakakis
242 0ace35e4 Christos Stavrakakis
243 1cbe2522 Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log,
244 1cbe2522 Christos Stavrakakis
                serializations=["json"])
245 7d368e73 Christos Stavrakakis
def list_floating_ip_pools(request):
246 b6bc4afa Christos Stavrakakis
    networks = Network.objects.filter(public=True, floating_ip_pool=True)
247 b6bc4afa Christos Stavrakakis
    networks = utils.filter_modified_since(request, objects=networks)
248 0ace35e4 Christos Stavrakakis
    pools = map(network_to_pool, networks)
249 7d368e73 Christos Stavrakakis
    request.serialization = "json"
250 7d368e73 Christos Stavrakakis
    data = json.dumps({"floating_ip_pools": pools})
251 7d368e73 Christos Stavrakakis
    request.serialization = "json"
252 7d368e73 Christos Stavrakakis
    return HttpResponse(data, status=200)