Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / subnets.py @ 883c1f94

History | View | Annotate | Download (9.4 kB)

1 16f7d0d9 Dionysis Grigoropoulos
# Copyright 2013 GRNET S.A. All rights reserved.
2 16f7d0d9 Dionysis Grigoropoulos
#
3 16f7d0d9 Dionysis Grigoropoulos
# Redistribution and use in source and binary forms, with or
4 16f7d0d9 Dionysis Grigoropoulos
# without modification, are permitted provided that the following
5 16f7d0d9 Dionysis Grigoropoulos
# conditions are met:
6 16f7d0d9 Dionysis Grigoropoulos
#
7 16f7d0d9 Dionysis Grigoropoulos
#   1. Redistributions of source code must retain the above
8 16f7d0d9 Dionysis Grigoropoulos
#      copyright notice, this list of conditions and the following
9 16f7d0d9 Dionysis Grigoropoulos
#      disclaimer.
10 16f7d0d9 Dionysis Grigoropoulos
#
11 16f7d0d9 Dionysis Grigoropoulos
#   2. Redistributions in binary form must reproduce the above
12 16f7d0d9 Dionysis Grigoropoulos
#      copyright notice, this list of conditions and the following
13 16f7d0d9 Dionysis Grigoropoulos
#      disclaimer in the documentation and/or other materials
14 16f7d0d9 Dionysis Grigoropoulos
#      provided with the distribution.
15 16f7d0d9 Dionysis Grigoropoulos
#
16 16f7d0d9 Dionysis Grigoropoulos
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 16f7d0d9 Dionysis Grigoropoulos
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 16f7d0d9 Dionysis Grigoropoulos
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 16f7d0d9 Dionysis Grigoropoulos
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 16f7d0d9 Dionysis Grigoropoulos
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 16f7d0d9 Dionysis Grigoropoulos
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 16f7d0d9 Dionysis Grigoropoulos
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 16f7d0d9 Dionysis Grigoropoulos
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 16f7d0d9 Dionysis Grigoropoulos
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 16f7d0d9 Dionysis Grigoropoulos
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 16f7d0d9 Dionysis Grigoropoulos
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 16f7d0d9 Dionysis Grigoropoulos
# POSSIBILITY OF SUCH DAMAGE.
28 16f7d0d9 Dionysis Grigoropoulos
#
29 16f7d0d9 Dionysis Grigoropoulos
# The views and conclusions contained in the software and
30 16f7d0d9 Dionysis Grigoropoulos
# documentation are those of the authors and should not be
31 16f7d0d9 Dionysis Grigoropoulos
# interpreted as representing official policies, either expressed
32 16f7d0d9 Dionysis Grigoropoulos
# or implied, of GRNET S.A.
33 16f7d0d9 Dionysis Grigoropoulos
34 16f7d0d9 Dionysis Grigoropoulos
from logging import getLogger
35 16f7d0d9 Dionysis Grigoropoulos
from snf_django.lib import api
36 16f7d0d9 Dionysis Grigoropoulos
37 6a959c73 Dionysis Grigoropoulos
from django.conf.urls import patterns
38 16f7d0d9 Dionysis Grigoropoulos
from django.http import HttpResponse
39 16f7d0d9 Dionysis Grigoropoulos
from django.utils import simplejson as json
40 883c1f94 Christos Stavrakakis
from django.db.models import Q
41 16f7d0d9 Dionysis Grigoropoulos
42 16f7d0d9 Dionysis Grigoropoulos
from snf_django.lib.api import utils
43 883c1f94 Christos Stavrakakis
from synnefo.db.models import Subnet
44 b7311f3d Dionysis Grigoropoulos
from synnefo.logic import subnets
45 24ea227b Dionysis Grigoropoulos
from synnefo.api import util
46 16f7d0d9 Dionysis Grigoropoulos
47 b7311f3d Dionysis Grigoropoulos
import ipaddr
48 16f7d0d9 Dionysis Grigoropoulos
49 16f7d0d9 Dionysis Grigoropoulos
log = getLogger(__name__)
50 16f7d0d9 Dionysis Grigoropoulos
51 16f7d0d9 Dionysis Grigoropoulos
52 6a959c73 Dionysis Grigoropoulos
urlpatterns = patterns(
53 6a959c73 Dionysis Grigoropoulos
    'synnefo.api.subnets',
54 6a959c73 Dionysis Grigoropoulos
    (r'^(?:/|.json|.xml)?$', 'demux'),
55 6a959c73 Dionysis Grigoropoulos
    (r'^/([-\w]+)(?:/|.json|.xml)?$', 'subnet_demux'))
56 6a959c73 Dionysis Grigoropoulos
57 6a959c73 Dionysis Grigoropoulos
58 16f7d0d9 Dionysis Grigoropoulos
def demux(request):
59 16f7d0d9 Dionysis Grigoropoulos
    if request.method == 'GET':
60 16f7d0d9 Dionysis Grigoropoulos
        return list_subnets(request)
61 16f7d0d9 Dionysis Grigoropoulos
    elif request.method == 'POST':
62 16f7d0d9 Dionysis Grigoropoulos
        return create_subnet(request)
63 16f7d0d9 Dionysis Grigoropoulos
    else:
64 16f7d0d9 Dionysis Grigoropoulos
        return api.api_method_not_allowed(request)
65 16f7d0d9 Dionysis Grigoropoulos
66 16f7d0d9 Dionysis Grigoropoulos
67 16f7d0d9 Dionysis Grigoropoulos
def subnet_demux(request, sub_id):
68 16f7d0d9 Dionysis Grigoropoulos
    if request.method == 'GET':
69 16f7d0d9 Dionysis Grigoropoulos
        return get_subnet(request, sub_id)
70 16f7d0d9 Dionysis Grigoropoulos
    elif request.method == 'DELETE':
71 16f7d0d9 Dionysis Grigoropoulos
        return delete_subnet(request, sub_id)
72 16f7d0d9 Dionysis Grigoropoulos
    elif request.method == 'PUT':
73 16f7d0d9 Dionysis Grigoropoulos
        return update_subnet(request, sub_id)
74 16f7d0d9 Dionysis Grigoropoulos
    else:
75 16f7d0d9 Dionysis Grigoropoulos
        return api.api_method_not_allowed(request)
76 16f7d0d9 Dionysis Grigoropoulos
77 16f7d0d9 Dionysis Grigoropoulos
78 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='GET', user_required=True, logger=log)
79 16f7d0d9 Dionysis Grigoropoulos
def list_subnets(request):
80 16f7d0d9 Dionysis Grigoropoulos
    """List all subnets of a user"""
81 883c1f94 Christos Stavrakakis
    userid = request.user_uniq
82 883c1f94 Christos Stavrakakis
    subnets_list = Subnet.objects.filter(Q(network__public=True) |
83 883c1f94 Christos Stavrakakis
                                         (Q(network__userid=userid) &
84 883c1f94 Christos Stavrakakis
                                          Q(network__public=False)))\
85 883c1f94 Christos Stavrakakis
                                 .order_by("id")
86 883c1f94 Christos Stavrakakis
    subnets_list = subnets_list.prefetch_related("ip_pools")\
87 883c1f94 Christos Stavrakakis
                               .select_related("network")
88 883c1f94 Christos Stavrakakis
    subnets_list = api.utils.filter_modified_since(request,
89 883c1f94 Christos Stavrakakis
                                                   objects=subnets_list)
90 883c1f94 Christos Stavrakakis
91 883c1f94 Christos Stavrakakis
    subnets_dict = [subnet_to_dict(sub) for sub in subnets_list]
92 5d83d2ff Dionysis Grigoropoulos
93 16f7d0d9 Dionysis Grigoropoulos
    data = json.dumps({'subnets': subnets_dict})
94 16f7d0d9 Dionysis Grigoropoulos
95 16f7d0d9 Dionysis Grigoropoulos
    return HttpResponse(data, status=200)
96 16f7d0d9 Dionysis Grigoropoulos
97 16f7d0d9 Dionysis Grigoropoulos
98 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='POST', user_required=True, logger=log)
99 16f7d0d9 Dionysis Grigoropoulos
def create_subnet(request):
100 316787ab Dionysis Grigoropoulos
    """Create a subnet
101 a996065e Dionysis Grigoropoulos
    network_id and the desired cidr are mandatory, everything else is optional
102 316787ab Dionysis Grigoropoulos

103 a996065e Dionysis Grigoropoulos
    """
104 16f7d0d9 Dionysis Grigoropoulos
    dictionary = utils.get_request_dict(request)
105 16f7d0d9 Dionysis Grigoropoulos
    log.info('create subnet %s', dictionary)
106 16f7d0d9 Dionysis Grigoropoulos
107 16f7d0d9 Dionysis Grigoropoulos
    try:
108 16f7d0d9 Dionysis Grigoropoulos
        subnet = dictionary['subnet']
109 16f7d0d9 Dionysis Grigoropoulos
        network_id = subnet['network_id']
110 16f7d0d9 Dionysis Grigoropoulos
        cidr = subnet['cidr']
111 16f7d0d9 Dionysis Grigoropoulos
    except KeyError:
112 16f7d0d9 Dionysis Grigoropoulos
        raise api.faults.BadRequest("Malformed request")
113 16f7d0d9 Dionysis Grigoropoulos
114 5d83d2ff Dionysis Grigoropoulos
    name = subnet.get('name', None)
115 5d83d2ff Dionysis Grigoropoulos
    ipversion = subnet.get('ip_version', 4)
116 b7311f3d Dionysis Grigoropoulos
117 210e5933 Christos Stavrakakis
    allocation_pools = subnet.get('allocation_pools', None)
118 210e5933 Christos Stavrakakis
    if allocation_pools is not None:
119 210e5933 Christos Stavrakakis
        allocation_pools = parse_ip_pools(allocation_pools)
120 210e5933 Christos Stavrakakis
121 b7311f3d Dionysis Grigoropoulos
    try:
122 b7311f3d Dionysis Grigoropoulos
        cidr_ip = ipaddr.IPNetwork(cidr)
123 b7311f3d Dionysis Grigoropoulos
    except ValueError:
124 210e5933 Christos Stavrakakis
        raise api.faults.BadRequest("Malformed CIDR '%s'" % cidr)
125 b7311f3d Dionysis Grigoropoulos
126 210e5933 Christos Stavrakakis
    # If no gateway is specified, send an empty string, because None is used
127 210e5933 Christos Stavrakakis
    # if the user wants no gateway at all
128 210e5933 Christos Stavrakakis
    gateway = subnet.get('gateway_ip', "")
129 b7311f3d Dionysis Grigoropoulos
    if gateway is "":
130 210e5933 Christos Stavrakakis
        gateway = str(cidr_ip.network + 1)
131 b7311f3d Dionysis Grigoropoulos
132 5d83d2ff Dionysis Grigoropoulos
    dhcp = subnet.get('enable_dhcp', True)
133 b7311f3d Dionysis Grigoropoulos
    slaac = subnet.get('enable_slaac', None)
134 b7311f3d Dionysis Grigoropoulos
135 b7311f3d Dionysis Grigoropoulos
    if ipversion == 6:
136 b7311f3d Dionysis Grigoropoulos
        if slaac is not None:
137 b7311f3d Dionysis Grigoropoulos
            dhcp = check_boolean_value(slaac, "enable_slaac")
138 b7311f3d Dionysis Grigoropoulos
        else:
139 b7311f3d Dionysis Grigoropoulos
            dhcp = check_boolean_value(dhcp, "dhcp")
140 b7311f3d Dionysis Grigoropoulos
    else:
141 b7311f3d Dionysis Grigoropoulos
        dhcp = check_boolean_value(dhcp, "dhcp")
142 b7311f3d Dionysis Grigoropoulos
143 5d83d2ff Dionysis Grigoropoulos
    dns = subnet.get('dns_nameservers', None)
144 5d83d2ff Dionysis Grigoropoulos
    hosts = subnet.get('host_routes', None)
145 5d83d2ff Dionysis Grigoropoulos
146 9cd6d7e9 Dionysis Grigoropoulos
    sub = subnets.create_subnet(network_id=network_id,
147 9cd6d7e9 Dionysis Grigoropoulos
                                cidr=cidr,
148 9cd6d7e9 Dionysis Grigoropoulos
                                name=name,
149 9cd6d7e9 Dionysis Grigoropoulos
                                ipversion=ipversion,
150 9cd6d7e9 Dionysis Grigoropoulos
                                gateway=gateway,
151 9cd6d7e9 Dionysis Grigoropoulos
                                dhcp=dhcp,
152 b7311f3d Dionysis Grigoropoulos
                                slaac=slaac,
153 9cd6d7e9 Dionysis Grigoropoulos
                                dns_nameservers=dns,
154 9cd6d7e9 Dionysis Grigoropoulos
                                allocation_pools=allocation_pools,
155 9cd6d7e9 Dionysis Grigoropoulos
                                host_routes=hosts,
156 9cd6d7e9 Dionysis Grigoropoulos
                                user_id=request.user_uniq)
157 16f7d0d9 Dionysis Grigoropoulos
158 16f7d0d9 Dionysis Grigoropoulos
    subnet_dict = subnet_to_dict(sub)
159 16f7d0d9 Dionysis Grigoropoulos
    data = json.dumps({'subnet': subnet_dict})
160 ef761fe4 Dionysis Grigoropoulos
    return HttpResponse(data, status=201)
161 16f7d0d9 Dionysis Grigoropoulos
162 16f7d0d9 Dionysis Grigoropoulos
163 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='GET', user_required=True, logger=log)
164 16f7d0d9 Dionysis Grigoropoulos
def get_subnet(request, sub_id):
165 16f7d0d9 Dionysis Grigoropoulos
    """Show info of a specific subnet"""
166 16f7d0d9 Dionysis Grigoropoulos
    user_id = request.user_uniq
167 883c1f94 Christos Stavrakakis
    subnet = subnets.get_subnet(sub_id, prefetch_related=True)
168 16f7d0d9 Dionysis Grigoropoulos
169 5b9f9c52 Dionysis Grigoropoulos
    if (subnet.network.userid != user_id) and (subnet.network.public is False):
170 cad4793e Dionysis Grigoropoulos
        raise api.faults.Unauthorized("You're not allowed to view this subnet")
171 16f7d0d9 Dionysis Grigoropoulos
172 5d83d2ff Dionysis Grigoropoulos
    subnet_dict = subnet_to_dict(subnet)
173 16f7d0d9 Dionysis Grigoropoulos
    data = json.dumps({'subnet': subnet_dict})
174 16f7d0d9 Dionysis Grigoropoulos
    return HttpResponse(data, status=200)
175 16f7d0d9 Dionysis Grigoropoulos
176 16f7d0d9 Dionysis Grigoropoulos
177 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='DELETE', user_required=True, logger=log)
178 16f7d0d9 Dionysis Grigoropoulos
def delete_subnet(request, sub_id):
179 316787ab Dionysis Grigoropoulos
    """Delete a subnet, raises BadRequest
180 16f7d0d9 Dionysis Grigoropoulos
    A subnet is deleted ONLY when the network that it belongs to is deleted
181 316787ab Dionysis Grigoropoulos

182 16f7d0d9 Dionysis Grigoropoulos
    """
183 16f7d0d9 Dionysis Grigoropoulos
    raise api.faults.BadRequest("Deletion of a subnet is not supported")
184 16f7d0d9 Dionysis Grigoropoulos
185 16f7d0d9 Dionysis Grigoropoulos
186 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='PUT', user_required=True, logger=log)
187 16f7d0d9 Dionysis Grigoropoulos
def update_subnet(request, sub_id):
188 316787ab Dionysis Grigoropoulos
    """Update the fields of a subnet
189 16f7d0d9 Dionysis Grigoropoulos
    Only the name can be updated, everything else returns BadRequest
190 316787ab Dionysis Grigoropoulos

191 16f7d0d9 Dionysis Grigoropoulos
    """
192 16f7d0d9 Dionysis Grigoropoulos
193 16f7d0d9 Dionysis Grigoropoulos
    dictionary = utils.get_request_dict(request)
194 16f7d0d9 Dionysis Grigoropoulos
    user_id = request.user_uniq
195 16f7d0d9 Dionysis Grigoropoulos
196 16f7d0d9 Dionysis Grigoropoulos
    try:
197 16f7d0d9 Dionysis Grigoropoulos
        subnet = dictionary['subnet']
198 16f7d0d9 Dionysis Grigoropoulos
    except KeyError:
199 16f7d0d9 Dionysis Grigoropoulos
        raise api.faults.BadRequest("Malformed request")
200 16f7d0d9 Dionysis Grigoropoulos
201 5d83d2ff Dionysis Grigoropoulos
    if len(subnet) != 1 or "name" not in subnet:
202 16f7d0d9 Dionysis Grigoropoulos
        raise api.faults.BadRequest("Only the name of subnet can be updated")
203 16f7d0d9 Dionysis Grigoropoulos
204 16f7d0d9 Dionysis Grigoropoulos
    name = subnet.get("name", None)
205 16f7d0d9 Dionysis Grigoropoulos
206 8646e606 Dionysis Grigoropoulos
    subnet_dict = subnet_to_dict(subnets.update_subnet(sub_id, name, user_id))
207 16f7d0d9 Dionysis Grigoropoulos
    data = json.dumps({'subnet': subnet_dict})
208 16f7d0d9 Dionysis Grigoropoulos
    return HttpResponse(data, status=200)
209 16f7d0d9 Dionysis Grigoropoulos
210 16f7d0d9 Dionysis Grigoropoulos
211 16f7d0d9 Dionysis Grigoropoulos
#Utility functions
212 16f7d0d9 Dionysis Grigoropoulos
def subnet_to_dict(subnet):
213 16f7d0d9 Dionysis Grigoropoulos
    """Returns a dictionary containing the info of a subnet"""
214 911a1bc1 Dionysis Grigoropoulos
    dns = check_empty_lists(subnet.dns_nameservers)
215 911a1bc1 Dionysis Grigoropoulos
    hosts = check_empty_lists(subnet.host_routes)
216 210e5933 Christos Stavrakakis
217 210e5933 Christos Stavrakakis
    allocation_pools = [render_ip_pool(pool)
218 210e5933 Christos Stavrakakis
                        for pool in subnet.ip_pools.all()]
219 210e5933 Christos Stavrakakis
220 210e5933 Christos Stavrakakis
    network = subnet.network
221 210e5933 Christos Stavrakakis
    d = {'id': str(subnet.id),
222 883c1f94 Christos Stavrakakis
         'network_id': str(subnet.network_id),
223 210e5933 Christos Stavrakakis
         'name': subnet.name if subnet.name is not None else "",
224 210e5933 Christos Stavrakakis
         'tenant_id': network.userid,
225 210e5933 Christos Stavrakakis
         'user_id': network.userid,
226 210e5933 Christos Stavrakakis
         'gateway_ip': subnet.gateway,
227 210e5933 Christos Stavrakakis
         'ip_version': subnet.ipversion,
228 210e5933 Christos Stavrakakis
         'cidr': subnet.cidr,
229 210e5933 Christos Stavrakakis
         'enable_dhcp': subnet.dhcp,
230 210e5933 Christos Stavrakakis
         'dns_nameservers': dns,
231 210e5933 Christos Stavrakakis
         'host_routes': hosts,
232 210e5933 Christos Stavrakakis
         'allocation_pools': allocation_pools}
233 97ca522f Dionysis Grigoropoulos
234 97ca522f Dionysis Grigoropoulos
    if subnet.ipversion == 6:
235 210e5933 Christos Stavrakakis
        d['enable_slaac'] = subnet.dhcp
236 210e5933 Christos Stavrakakis
237 210e5933 Christos Stavrakakis
    d['links'] = util.subnet_to_links(subnet.id)
238 97ca522f Dionysis Grigoropoulos
239 210e5933 Christos Stavrakakis
    return d
240 16f7d0d9 Dionysis Grigoropoulos
241 16f7d0d9 Dionysis Grigoropoulos
242 210e5933 Christos Stavrakakis
def render_ip_pool(pool):
243 210e5933 Christos Stavrakakis
    network = ipaddr.IPNetwork(pool.base).network
244 210e5933 Christos Stavrakakis
    start = str(network + pool.offset)
245 210e5933 Christos Stavrakakis
    end = str(network + pool.offset + pool.size - 1)
246 210e5933 Christos Stavrakakis
    return {"start": start, "end": end}
247 210e5933 Christos Stavrakakis
248 210e5933 Christos Stavrakakis
249 210e5933 Christos Stavrakakis
def parse_ip_pools(pools):
250 210e5933 Christos Stavrakakis
    """Convert [{'start': '192.168.42.1', 'end': '192.168.42.15'},
251 210e5933 Christos Stavrakakis
             {'start': '192.168.42.30', 'end': '192.168.42.60'}]
252 4445f97a Dionysis Grigoropoulos
    to
253 210e5933 Christos Stavrakakis
            [(IPv4Address("192.168.42.1"), IPv4Address("192.168.42.15")),
254 210e5933 Christos Stavrakakis
             (IPv4Address("192.168.42.30"), IPv4Address("192.168.42.60"))]
255 316787ab Dionysis Grigoropoulos

256 4445f97a Dionysis Grigoropoulos
    """
257 210e5933 Christos Stavrakakis
    try:
258 210e5933 Christos Stavrakakis
        return sorted([(ipaddr.IPv4Address(p["start"]),
259 210e5933 Christos Stavrakakis
                        ipaddr.IPv4Address(p["end"])) for p in pools])
260 210e5933 Christos Stavrakakis
    except KeyError:
261 210e5933 Christos Stavrakakis
        raise api.faults.BadRequest("Malformed allocation pool.")
262 210e5933 Christos Stavrakakis
    except ipaddr.AddressValueError:
263 210e5933 Christos Stavrakakis
        raise api.faults.BadRequest("Allocation pools contain invalid IPv4"
264 210e5933 Christos Stavrakakis
                                    " address")
265 4445f97a Dionysis Grigoropoulos
266 4445f97a Dionysis Grigoropoulos
267 911a1bc1 Dionysis Grigoropoulos
def check_empty_lists(value):
268 911a1bc1 Dionysis Grigoropoulos
    """Check if value is Null/None, in which case we return an empty list"""
269 911a1bc1 Dionysis Grigoropoulos
    if value is None:
270 911a1bc1 Dionysis Grigoropoulos
        return []
271 911a1bc1 Dionysis Grigoropoulos
    return value
272 911a1bc1 Dionysis Grigoropoulos
273 911a1bc1 Dionysis Grigoropoulos
274 b7311f3d Dionysis Grigoropoulos
def check_boolean_value(value, key):
275 b7311f3d Dionysis Grigoropoulos
    """Check if dhcp value is in acceptable values"""
276 b7311f3d Dionysis Grigoropoulos
    if value not in [True, False]:
277 b7311f3d Dionysis Grigoropoulos
        raise api.faults.BadRequest("Malformed request, %s must "
278 b7311f3d Dionysis Grigoropoulos
                                    "be True or False" % key)
279 b7311f3d Dionysis Grigoropoulos
    return value