Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (9.3 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 9115d567 Christos Stavrakakis
from synnefo.db.models import Network, FloatingIP
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 59183afc Christos Stavrakakis
        return 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 59183afc Christos Stavrakakis
        return 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 b6bc4afa Christos Stavrakakis
    floating_ips = FloatingIP.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 59183afc Christos Stavrakakis
        floating_ip = FloatingIP.objects.get(id=floating_ip_id,
113 59183afc Christos Stavrakakis
                                             deleted=False,
114 59183afc Christos Stavrakakis
                                             userid=userid)
115 59183afc Christos Stavrakakis
    except FloatingIP.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 9e8be4fb Christos Stavrakakis
                if FloatingIP.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 9e8be4fb Christos Stavrakakis
                elif not network.nics.filter(ipv4=address,
173 32e4e343 Christos Stavrakakis
                                             machine__userid=userid).exists():
174 9e8be4fb Christos Stavrakakis
                        msg = "Address '%s' is already in use" % address
175 9e8be4fb Christos Stavrakakis
                        raise faults.Conflict(msg)
176 59183afc Christos Stavrakakis
        floating_ip = FloatingIP.objects.create(ipv4=address, network=network,
177 a3d99af0 Christos Stavrakakis
                                                userid=userid, machine=machine)
178 59183afc Christos Stavrakakis
        quotas.issue_and_accept_commission(floating_ip)
179 59183afc Christos Stavrakakis
    except:
180 59183afc Christos Stavrakakis
        transaction.rollback()
181 59183afc Christos Stavrakakis
        raise
182 59183afc Christos Stavrakakis
    else:
183 59183afc Christos Stavrakakis
        transaction.commit()
184 59183afc Christos Stavrakakis
185 59183afc Christos Stavrakakis
    log.info("User '%s' allocated floating IP '%s", userid, floating_ip)
186 59183afc Christos Stavrakakis
187 59183afc Christos Stavrakakis
    request.serialization = "json"
188 59183afc Christos Stavrakakis
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
189 59183afc Christos Stavrakakis
    return HttpResponse(data, status=200)
190 59183afc Christos Stavrakakis
191 59183afc Christos Stavrakakis
192 1cbe2522 Christos Stavrakakis
@api.api_method(http_method='DELETE', user_required=True, logger=log,
193 1cbe2522 Christos Stavrakakis
                serializations=["json"])
194 59183afc Christos Stavrakakis
@transaction.commit_on_success
195 59183afc Christos Stavrakakis
def release_floating_ip(request, floating_ip_id):
196 59183afc Christos Stavrakakis
    """Release a floating IP."""
197 59183afc Christos Stavrakakis
    userid = request.user_uniq
198 59183afc Christos Stavrakakis
    log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
199 59183afc Christos Stavrakakis
    try:
200 59183afc Christos Stavrakakis
        floating_ip = FloatingIP.objects.select_for_update()\
201 59183afc Christos Stavrakakis
                                        .get(id=floating_ip_id,
202 59183afc Christos Stavrakakis
                                             deleted=False,
203 59183afc Christos Stavrakakis
                                             userid=userid)
204 59183afc Christos Stavrakakis
    except FloatingIP.DoesNotExist:
205 59183afc Christos Stavrakakis
        raise faults.ItemNotFound("Floating IP '%s' does not exist" %
206 59183afc Christos Stavrakakis
                                  floating_ip_id)
207 59183afc Christos Stavrakakis
208 72dea98f Christos Stavrakakis
    # Since we have got an exlusively lock in floating IP, and since
209 72dea98f Christos Stavrakakis
    # to remove a floating IP you need the same lock, the in_use() query
210 72dea98f Christos Stavrakakis
    # is safe
211 59183afc Christos Stavrakakis
    if floating_ip.in_use():
212 59183afc Christos Stavrakakis
        msg = "Floating IP '%s' is used" % floating_ip.id
213 59183afc Christos Stavrakakis
        raise faults.Conflict(message=msg)
214 59183afc Christos Stavrakakis
215 59183afc Christos Stavrakakis
    try:
216 59183afc Christos Stavrakakis
        floating_ip.network.release_address(floating_ip.ipv4)
217 59183afc Christos Stavrakakis
        floating_ip.deleted = True
218 59183afc Christos Stavrakakis
        quotas.issue_and_accept_commission(floating_ip, delete=True)
219 59183afc Christos Stavrakakis
    except:
220 59183afc Christos Stavrakakis
        transaction.rollback()
221 59183afc Christos Stavrakakis
        raise
222 59183afc Christos Stavrakakis
    else:
223 59183afc Christos Stavrakakis
        floating_ip.delete()
224 59183afc Christos Stavrakakis
        transaction.commit()
225 59183afc Christos Stavrakakis
226 59183afc Christos Stavrakakis
    log.info("User '%s' released IP '%s", userid, floating_ip)
227 59183afc Christos Stavrakakis
228 59183afc Christos Stavrakakis
    return HttpResponse(status=204)
229 7d368e73 Christos Stavrakakis
230 7d368e73 Christos Stavrakakis
231 0ace35e4 Christos Stavrakakis
def network_to_pool(network):
232 0ace35e4 Christos Stavrakakis
    pool = network.get_pool(with_lock=False)
233 0ace35e4 Christos Stavrakakis
    return {"name": str(network.id),
234 0ace35e4 Christos Stavrakakis
            "size": pool.pool_size,
235 0ace35e4 Christos Stavrakakis
            "free": pool.count_available()}
236 0ace35e4 Christos Stavrakakis
237 0ace35e4 Christos Stavrakakis
238 1cbe2522 Christos Stavrakakis
@api.api_method(http_method='GET', user_required=True, logger=log,
239 1cbe2522 Christos Stavrakakis
                serializations=["json"])
240 7d368e73 Christos Stavrakakis
def list_floating_ip_pools(request):
241 b6bc4afa Christos Stavrakakis
    networks = Network.objects.filter(public=True, floating_ip_pool=True)
242 b6bc4afa Christos Stavrakakis
    networks = utils.filter_modified_since(request, objects=networks)
243 0ace35e4 Christos Stavrakakis
    pools = map(network_to_pool, networks)
244 7d368e73 Christos Stavrakakis
    request.serialization = "json"
245 7d368e73 Christos Stavrakakis
    data = json.dumps({"floating_ip_pools": pools})
246 7d368e73 Christos Stavrakakis
    request.serialization = "json"
247 7d368e73 Christos Stavrakakis
    return HttpResponse(data, status=200)