Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / subnets.py @ 406968dc

History | View | Annotate | Download (8.9 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 16f7d0d9 Dionysis Grigoropoulos
41 16f7d0d9 Dionysis Grigoropoulos
from snf_django.lib.api import utils
42 210e5933 Christos Stavrakakis
#from synnefo.db.models import Subnet
43 b7311f3d Dionysis Grigoropoulos
from synnefo.logic import subnets
44 24ea227b Dionysis Grigoropoulos
from synnefo.api import util
45 16f7d0d9 Dionysis Grigoropoulos
46 b7311f3d Dionysis Grigoropoulos
import ipaddr
47 16f7d0d9 Dionysis Grigoropoulos
48 16f7d0d9 Dionysis Grigoropoulos
log = getLogger(__name__)
49 16f7d0d9 Dionysis Grigoropoulos
50 16f7d0d9 Dionysis Grigoropoulos
51 6a959c73 Dionysis Grigoropoulos
urlpatterns = patterns(
52 6a959c73 Dionysis Grigoropoulos
    'synnefo.api.subnets',
53 6a959c73 Dionysis Grigoropoulos
    (r'^(?:/|.json|.xml)?$', 'demux'),
54 6a959c73 Dionysis Grigoropoulos
    (r'^/([-\w]+)(?:/|.json|.xml)?$', 'subnet_demux'))
55 6a959c73 Dionysis Grigoropoulos
56 6a959c73 Dionysis Grigoropoulos
57 16f7d0d9 Dionysis Grigoropoulos
def demux(request):
58 16f7d0d9 Dionysis Grigoropoulos
    if request.method == 'GET':
59 16f7d0d9 Dionysis Grigoropoulos
        return list_subnets(request)
60 16f7d0d9 Dionysis Grigoropoulos
    elif request.method == 'POST':
61 16f7d0d9 Dionysis Grigoropoulos
        return create_subnet(request)
62 16f7d0d9 Dionysis Grigoropoulos
    else:
63 16f7d0d9 Dionysis Grigoropoulos
        return api.api_method_not_allowed(request)
64 16f7d0d9 Dionysis Grigoropoulos
65 16f7d0d9 Dionysis Grigoropoulos
66 16f7d0d9 Dionysis Grigoropoulos
def subnet_demux(request, sub_id):
67 16f7d0d9 Dionysis Grigoropoulos
    if request.method == 'GET':
68 16f7d0d9 Dionysis Grigoropoulos
        return get_subnet(request, sub_id)
69 16f7d0d9 Dionysis Grigoropoulos
    elif request.method == 'DELETE':
70 16f7d0d9 Dionysis Grigoropoulos
        return delete_subnet(request, sub_id)
71 16f7d0d9 Dionysis Grigoropoulos
    elif request.method == 'PUT':
72 16f7d0d9 Dionysis Grigoropoulos
        return update_subnet(request, sub_id)
73 16f7d0d9 Dionysis Grigoropoulos
    else:
74 16f7d0d9 Dionysis Grigoropoulos
        return api.api_method_not_allowed(request)
75 16f7d0d9 Dionysis Grigoropoulos
76 16f7d0d9 Dionysis Grigoropoulos
77 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='GET', user_required=True, logger=log)
78 16f7d0d9 Dionysis Grigoropoulos
def list_subnets(request):
79 16f7d0d9 Dionysis Grigoropoulos
    """List all subnets of a user"""
80 5d83d2ff Dionysis Grigoropoulos
    subnet_list = subnets.list_subnets(request.user_uniq)
81 5d83d2ff Dionysis Grigoropoulos
    subnets_dict = [subnet_to_dict(sub)
82 5d83d2ff Dionysis Grigoropoulos
                    for sub in subnet_list.order_by('id')]
83 5d83d2ff Dionysis Grigoropoulos
84 16f7d0d9 Dionysis Grigoropoulos
    data = json.dumps({'subnets': subnets_dict})
85 16f7d0d9 Dionysis Grigoropoulos
86 16f7d0d9 Dionysis Grigoropoulos
    return HttpResponse(data, status=200)
87 16f7d0d9 Dionysis Grigoropoulos
88 16f7d0d9 Dionysis Grigoropoulos
89 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='POST', user_required=True, logger=log)
90 16f7d0d9 Dionysis Grigoropoulos
def create_subnet(request):
91 316787ab Dionysis Grigoropoulos
    """Create a subnet
92 a996065e Dionysis Grigoropoulos
    network_id and the desired cidr are mandatory, everything else is optional
93 316787ab Dionysis Grigoropoulos

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

173 16f7d0d9 Dionysis Grigoropoulos
    """
174 16f7d0d9 Dionysis Grigoropoulos
    raise api.faults.BadRequest("Deletion of a subnet is not supported")
175 16f7d0d9 Dionysis Grigoropoulos
176 16f7d0d9 Dionysis Grigoropoulos
177 16f7d0d9 Dionysis Grigoropoulos
@api.api_method(http_method='PUT', user_required=True, logger=log)
178 16f7d0d9 Dionysis Grigoropoulos
def update_subnet(request, sub_id):
179 316787ab Dionysis Grigoropoulos
    """Update the fields of a subnet
180 16f7d0d9 Dionysis Grigoropoulos
    Only the name can be updated, everything else returns BadRequest
181 316787ab Dionysis Grigoropoulos

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

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