Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / networks.py @ 3878553d

History | View | Annotate | Download (10.4 kB)

1
# Copyright 2011-2012 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

    
36
from django.conf.urls.defaults import patterns
37
from django.conf import settings
38
from django.db.models import Q
39
from django.db import transaction
40
from django.http import HttpResponse
41
from django.template.loader import render_to_string
42
from django.utils import simplejson as json
43

    
44
from synnefo.api import util
45
from synnefo.api.actions import network_actions
46
from synnefo.api.common import method_not_allowed
47
from synnefo.api.faults import (BadRequest, Unauthorized,
48
                                NetworkInUse, OverLimit)
49
from synnefo.db.models import Network, Pool
50

    
51
from synnefo.logic import backend
52
from synnefo.settings import MAX_CIDR_BLOCK
53

    
54

    
55
log = getLogger('synnefo.api')
56

    
57
urlpatterns = patterns('synnefo.api.networks',
58
    (r'^(?:/|.json|.xml)?$', 'demux'),
59
    (r'^/detail(?:.json|.xml)?$', 'list_networks', {'detail': True}),
60
    (r'^/(\w+)(?:.json|.xml)?$', 'network_demux'),
61
    (r'^/(\w+)/action(?:.json|.xml)?$', 'network_action'),
62
)
63

    
64

    
65
def demux(request):
66
    if request.method == 'GET':
67
        return list_networks(request)
68
    elif request.method == 'POST':
69
        return create_network(request)
70
    else:
71
        return method_not_allowed(request)
72

    
73

    
74
def network_demux(request, network_id):
75
    if request.method == 'GET':
76
        return get_network_details(request, network_id)
77
    elif request.method == 'PUT':
78
        return update_network_name(request, network_id)
79
    elif request.method == 'DELETE':
80
        return delete_network(request, network_id)
81
    else:
82
        return method_not_allowed(request)
83

    
84

    
85
def network_to_dict(network, user_id, detail=True):
86
    network_id = str(network.id) if not network.public else 'public'
87
    d = {'id': network_id, 'name': network.name}
88
    if detail:
89
        d['cidr'] = network.subnet
90
        d['cidr6'] = network.subnet6
91
        d['gateway'] = network.gateway
92
        d['gateway6'] = network.gateway6
93
        d['dhcp'] = network.dhcp
94
        d['type'] = network.type
95
        d['updated'] = util.isoformat(network.updated)
96
        d['created'] = util.isoformat(network.created)
97
        d['status'] = network.state
98

    
99
        attachments = [util.construct_nic_id(nic) for nic in network.nics.filter(machine__userid= user_id)]
100
        d['attachments'] = {'values':attachments}
101
    return d
102

    
103

    
104
def render_network(request, networkdict, status=200):
105
    if request.serialization == 'xml':
106
        data = render_to_string('network.xml', {'network': networkdict})
107
    else:
108
        data = json.dumps({'network': networkdict})
109
    return HttpResponse(data, status=status)
110

    
111

    
112
@util.api_method('GET')
113
def list_networks(request, detail=False):
114
    # Normal Response Codes: 200, 203
115
    # Error Response Codes: computeFault (400, 500),
116
    #                       serviceUnavailable (503),
117
    #                       unauthorized (401),
118
    #                       badRequest (400),
119
    #                       overLimit (413)
120

    
121
    log.debug('list_networks detail=%s', detail)
122
    since = util.isoparse(request.GET.get('changes-since'))
123
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
124
                                           Q(public=True))
125

    
126
    if since:
127
        user_networks = user_networks.filter(updated__gte=since)
128
        if not user_networks:
129
            return HttpResponse(status=304)
130
    else:
131
        user_networks = user_networks.filter(deleted=False)
132

    
133
    networks = [network_to_dict(network, request.user_uniq, detail)
134
                for network in user_networks]
135

    
136
    if request.serialization == 'xml':
137
        data = render_to_string('list_networks.xml', {
138
            'networks': networks,
139
            'detail': detail})
140
    else:
141
        data = json.dumps({'networks': {'values': networks}})
142

    
143
    return HttpResponse(data, status=200)
144

    
145

    
146
@util.api_method('POST')
147
@transaction.commit_on_success
148
def create_network(request):
149
    # Normal Response Code: 202
150
    # Error Response Codes: computeFault (400, 500),
151
    #                       serviceUnavailable (503),
152
    #                       unauthorized (401),
153
    #                       badMediaType(415),
154
    #                       badRequest (400),
155
    #                       overLimit (413)
156

    
157
    req = util.get_request_dict(request)
158
    log.debug('create_network %s', req)
159

    
160
    try:
161
        d = req['network']
162
        name = d['name']
163
        # TODO: Fix this temp values:
164
        subnet = d.get('cidr', '192.168.1.0/24')
165
        subnet6 = d.get('cidr6', None)
166
        gateway = d.get('gateway', None)
167
        gateway6 = d.get('gateway6', None)
168
        typ = d.get('type', 'PRIVATE_MAC_FILTERED')
169
        dhcp = d.get('dhcp', True)
170
    except (KeyError, ValueError):
171
        raise BadRequest('Malformed request.')
172

    
173
    if typ == 'PUBLIC_ROUTED':
174
        raise Unauthorized('Can not create a public network.')
175

    
176
    user_networks = len(Network.objects.filter(userid=request.user_uniq,
177
                                               deleted=False))
178
    if user_networks == settings.MAX_NETWORKS_PER_USER:
179
        raise OverLimit('Network count limit exceeded for your account.')
180

    
181
    cidr_block = int(subnet.split('/')[1])
182
    if cidr_block <= MAX_CIDR_BLOCK:
183
        raise OverLimit("Network size is to big. Please specify a network"
184
                        " smaller than /" + str(MAX_CIDR_BLOCK) + '.')
185

    
186
    try:
187
        link = util.network_link_from_type(typ)
188
        if not link:
189
            raise Exception("Can not create network. No connectivity link.")
190

    
191
        network = Network.objects.create(
192
                name=name,
193
                userid=request.user_uniq,
194
                subnet=subnet,
195
                subnet6=subnet6,
196
                gateway=gateway,
197
                gateway6=gateway6,
198
                dhcp=dhcp,
199
                type=typ,
200
                link=link,
201
                action='CREATE',
202
                state='PENDING')
203
    except Pool.PoolExhausted:
204
        raise OverLimit('Network count limit exceeded.')
205

    
206
    backend.create_network(network)
207

    
208
    networkdict = network_to_dict(network, request.user_uniq)
209
    return render_network(request, networkdict, status=202)
210

    
211

    
212
@util.api_method('GET')
213
def get_network_details(request, network_id):
214
    # Normal Response Codes: 200, 203
215
    # Error Response Codes: computeFault (400, 500),
216
    #                       serviceUnavailable (503),
217
    #                       unauthorized (401),
218
    #                       badRequest (400),
219
    #                       itemNotFound (404),
220
    #                       overLimit (413)
221

    
222
    log.debug('get_network_details %s', network_id)
223
    net = util.get_network(network_id, request.user_uniq)
224
    netdict = network_to_dict(net, request.user_uniq)
225
    return render_network(request, netdict)
226

    
227

    
228
@util.api_method('PUT')
229
def update_network_name(request, network_id):
230
    # Normal Response Code: 204
231
    # Error Response Codes: computeFault (400, 500),
232
    #                       serviceUnavailable (503),
233
    #                       unauthorized (401),
234
    #                       badRequest (400),
235
    #                       badMediaType(415),
236
    #                       itemNotFound (404),
237
    #                       overLimit (413)
238

    
239
    req = util.get_request_dict(request)
240
    log.debug('update_network_name %s', network_id)
241

    
242
    try:
243
        name = req['network']['name']
244
    except (TypeError, KeyError):
245
        raise BadRequest('Malformed request.')
246

    
247
    net = util.get_network(network_id, request.user_uniq)
248
    if net.public:
249
        raise Unauthorized('Can not rename the public network.')
250
    net.name = name
251
    net.save()
252
    return HttpResponse(status=204)
253

    
254

    
255
@util.api_method('DELETE')
256
@transaction.commit_on_success
257
def delete_network(request, network_id):
258
    # Normal Response Code: 204
259
    # Error Response Codes: computeFault (400, 500),
260
    #                       serviceUnavailable (503),
261
    #                       unauthorized (401),
262
    #                       itemNotFound (404),
263
    #                       unauthorized (401),
264
    #                       overLimit (413)
265

    
266
    log.debug('delete_network %s', network_id)
267
    net = util.get_network(network_id, request.user_uniq)
268
    if net.public:
269
        raise Unauthorized('Can not delete the public network.')
270

    
271
    if net.machines.all():  # Nics attached on network
272
        raise NetworkInUse('Machines are connected to network.')
273

    
274
    net.action = 'DESTROY'
275
    net.save()
276

    
277
    backend.delete_network(net)
278
    return HttpResponse(status=204)
279

    
280

    
281
@util.api_method('POST')
282
def network_action(request, network_id):
283
    req = util.get_request_dict(request)
284
    log.debug('network_action %s %s', network_id, req)
285
    if len(req) != 1:
286
        raise BadRequest('Malformed request.')
287

    
288
    net = util.get_network(network_id, request.user_uniq)
289
    if net.public:
290
        raise Unauthorized('Can not modify the public network.')
291

    
292
    key = req.keys()[0]
293
    val = req[key]
294

    
295
    try:
296
        assert isinstance(val, dict)
297
        return network_actions[key](request, net, req[key])
298
    except KeyError:
299
        raise BadRequest('Unknown action.')
300
    except AssertionError:
301
        raise BadRequest('Invalid argument.')