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) |