Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / neutron / subnet_views.py @ efabd2d4

History | View | Annotate | Download (9.7 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from logging import getLogger
35
from snf_django.lib import api
36
from snf_django.lib.api import faults
37

    
38
from django.http import HttpResponse
39
from django.utils import simplejson as json
40

    
41
from snf_django.lib.api import utils
42
from models import Subnet, Network
43
from synnefo.logic import networks
44

    
45
from ipaddr import IPv4Network, IPv6Network
46

    
47
log = getLogger(__name__)
48

    
49

    
50
def demux(request):
51
    if request.method == 'GET':
52
        return list_subnets(request)
53
    elif request.method == 'POST':
54
        return create_subnet(request)
55
    else:
56
        return api.api_method_not_allowed(request)
57

    
58

    
59
def subnet_demux(request, sub_id):
60
    if request.method == 'GET':
61
        return get_subnet(request, sub_id)
62
    elif request.method == 'DELETE':
63
        return delete_subnet(request, sub_id)
64
    elif request.method == 'PUT':
65
        return update_subnet(request, sub_id)
66
    else:
67
        return api.api_method_not_allowed(request)
68

    
69

    
70
@api.api_method(http_method='GET', user_required=True, logger=log)
71
def list_subnets(request):
72
    '''List all subnets of a user'''
73
    log.debug('list_subnets')
74

    
75
    user_subnets = Subnet.objects.filter(network__userid=request.user_uniq)
76
    subnets_dict = [subnet_to_dict(sub)
77
                    for sub in user_subnets.order_by('id')]
78
    data = json.dumps({'subnets': subnets_dict})
79

    
80
    return HttpResponse(data, status=200)
81

    
82

    
83
@api.api_method(http_method='POST', user_required=True, logger=log)
84
def create_subnet(request):
85
    '''Create a subnet'''
86

    
87
    dictionary = utils.get_request_dict(request)
88
    log.info('create subnet %s', dictionary)
89
    user_id = request.user_uniq
90

    
91
    try:
92
        subnet = dictionary['subnet']
93
        network_id = subnet['network_id']
94
        cidr = subnet['cidr']
95
    except KeyError:
96
        raise api.faults.BadRequest("Malformed request")
97

    
98
    try:
99
        network = Network.objects.get(id=network_id)
100
    except Network.DoesNotExist:
101
        raise api.faults.ItemNotFound("No networks found with that id")
102

    
103
    if user_id != network.userid:
104
        raise api.faults.Unauthorized("Unauthorized operation")
105

    
106
    ipversion = subnet.get('ip_version', 4)
107
    if ipversion not in [4, 6]:
108
        raise api.faults.BadRequest("Malformed IP version type")
109

    
110
    dhcp = check_dhcp_value(subnet.get('enable_dhcp', True))
111
    name = check_name_length(subnet.get('name', None))
112

    
113
    # Returns the first available IP in the subnet
114
    if ipversion == 6:
115
        potential_gateway = str(IPv6Network(cidr).network + 1)
116
        check_number_of_subnets(network, 6)
117
    else:
118
        potential_gateway = str(IPv4Network(cidr).network + 1)
119
        check_number_of_subnets(network, 4)
120

    
121
    gateway = subnet.get('gateway_ip', potential_gateway)
122

    
123
    if ipversion == 6:
124
        networks.validate_network_params(None, None, cidr, gateway)
125
    else:
126
        networks.validate_network_params(cidr, gateway)
127

    
128
    check_for_hosts_dns(subnet)
129

    
130
    # FIX ME
131
    try:
132
        sub = Subnet.objects.create(name=name, network=network, cidr=cidr,
133
                                    ipversion=ipversion, gateway=gateway,
134
                                    dhcp=dhcp)
135
    except:
136
        return "Error"
137

    
138
    subnet_dict = subnet_to_dict(sub)
139
    data = json.dumps({'subnet': subnet_dict})
140
    return HttpResponse(data, status=200)
141

    
142

    
143
@api.api_method(http_method='GET', user_required=True, logger=log)
144
def get_subnet(request, sub_id):
145
    '''Show info of a specific subnet'''
146
    log.debug('get_subnet %s', sub_id)
147
    user_id = request.user_uniq
148

    
149
    try:
150
        subnet = Subnet.objects.get(id=sub_id)
151
    except Subnet.DoesNotExist:
152
        raise api.faults.ItemNotFound("Subnet not found")
153

    
154
    if subnet.network.userid != user_id:
155
        raise api.failts.Unauthorized("You're not allowed to view this subnet")
156

    
157
    subnet_dict = subnet_to_dict(subnet)
158
    data = json.dumps({'subnet': subnet_dict})
159
    return HttpResponse(data, status=200)
160

    
161

    
162
@api.api_method(http_method='DELETE', user_required=True, logger=log)
163
def delete_subnet(request, sub_id):
164
    '''Delete a subnet -- Operation not allowed'''
165
    raise api.faults.BadRequest("Deletion of a subnet is not supported")
166

    
167

    
168
@api.api_method(http_method='PUT', user_required=True, logger=log)
169
def update_subnet(request, sub_id):
170
    '''Update info of a subnet'''
171
    dictionary = utils.get_request_dict(request)
172
    log.info('Update subnet %s', dictionary)
173
    user_id = request.user_uniq
174

    
175
    try:
176
        subnet = dictionary['subnet']
177
    except KeyError:
178
        raise api.faults.BadRequest("Malformed request")
179

    
180
    original_subnet = get_subnet_fromdb(sub_id, user_id)
181
    original_dict = subnet_to_dict(original_subnet)
182

    
183
    if subnet.get('ip_version', None):
184
        raise api.faults.BadRequest("Malformed request, ip_version cannot be "
185
                                    "updated")
186
    if subnet.get('cidr', None):
187
        raise api.faults.BadRequest("Malformed request, cidr cannot be "
188
                                    "updated")
189
    if subnet.get('allocation_pools', None):
190
        raise api.faults.BadRequest("Malformed request, allocation pools "
191
                                    "cannot be updated")
192

    
193
    check_for_hosts_dns(subnet)
194
    name = subnet.get('name', original_dict['name'])
195
    if name:
196
        check_name_length(name)
197

    
198
    dhcp = subnet.get('enable_dhcp', original_dict['enable_dhcp'])
199
    check_dhcp_value(dhcp)
200

    
201
    gateway = subnet.get('gateway_ip', original_dict['gateway_ip'])
202
    #FIX ME, check if IP is in use
203
    if original_dict['ip_version'] == 6:
204
        networks.validate_network_params(None, None, original_dict['cidr'],
205
                                         gateway)
206
    else:
207
        networks.validate_network_params(original_dict['cidr'], gateway)
208

    
209
    try:
210
        original_subnet.gateway = gateway
211
        original_subnet.name = name
212
        original_subnet.dhcp = dhcp
213
        original_subnet.save()
214
    except:
215
        #Fix me
216
        return "Unknown Error"
217

    
218
    subnet_dict = subnet_to_dict(original_subnet)
219
    data = json.dumps({'subnet': subnet_dict})
220
    return HttpResponse(data, status=200)
221

    
222

    
223
#Utility functions
224
def subnet_to_dict(subnet):
225
    '''Returns a dictionary containing the info of a subnet'''
226
    # FIX ME, allocation pools
227
    dictionary = dict({'id': subnet.id, 'network_id': subnet.network.id,
228
                       'name': subnet.name, 'tenant_id': subnet.network.userid,
229
                       'gateway_ip': subnet.gateway,
230
                       'ip_version': subnet.ipversion, 'cidr': subnet.cidr,
231
                       'enable_dhcp': subnet.dhcp, 'dns_nameservers': [],
232
                       'host_routes': [], 'allocation_pools': []})
233
    return dictionary
234

    
235

    
236
def check_number_of_subnets(network, version):
237
    '''Check if a user can add a subnet in a network'''
238
    if network.subnet_set.filter(ipversion=version):
239
        raise api.faults.BadRequest("Only one subnet of IPv4/IPv6 per "
240
                                    "network is allowed")
241

    
242

    
243
def check_dhcp_value(dhcp):
244
    '''Check if dhcp value is in acceptable values'''
245
    if dhcp not in [True, False]:
246
        raise api.faults.BadRequest("Malformed request, enable_dhcp must be "
247
                                    "True or False")
248
    return dhcp
249

    
250

    
251
def check_name_length(name):
252
    '''Check if the length of a name is within acceptable value'''
253
    if len(str(name)) > Subnet.SUBNET_NAME_LENGTH:
254
        raise api.faults.BadRequest("Subnet name too long")
255
    return name
256

    
257

    
258
def check_for_hosts_dns(subnet):
259
    '''
260
    Check if a request contains host_routes or dns_nameservers options
261
    Expects the request in a dictionary format
262
    '''
263
    if subnet.get('host_routes', None):
264
        raise api.faults.BadRequest("Setting host routes isn't supported")
265
    if subnet.get('dns_nameservers', None):
266
        raise api.faults.BadRequest("Setting dns nameservers isn't supported")
267

    
268

    
269
def get_subnet_fromdb(subnet_id, user_id, for_update=False):
270
    '''
271
    Return a Subnet instance or raise ItemNotFound.
272
    This is the same as util.get_network
273
    '''
274
    try:
275
        subnet_id = int(subnet_id)
276
        if for_update:
277
            return Subnet.objects.select_for_update().get(id=subnet_id,
278
                                                          network__userid=
279
                                                          user_id)
280
        return Subnet.objects.get(id=subnet_id, network__userid=user_id)
281
    except (ValueError, Subnet.DoesNotExist):
282
        raise api.faults.ItemNotFound('Subnet not found.')