Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / networks.py @ 6dd70a5c

History | View | Annotate | Download (9.6 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.http import HttpResponse
40
from django.template.loader import render_to_string
41
from django.utils import simplejson as json
42

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

    
51

    
52
log = getLogger('synnefo.api')
53

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

    
61

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

    
70

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

    
81

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

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

    
100

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

    
108

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

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

    
124
    if since:
125
        user_networks = user_networks.filter(updated__gte=since)
126
        if not user_networks:
127
            return HttpResponse(status=304)
128

    
129
    networks = [network_to_dict(network, request.user_uniq, detail)
130
                for network in user_networks]
131

    
132
    if request.serialization == 'xml':
133
        data = render_to_string('list_networks.xml', {
134
            'networks': networks,
135
            'detail': detail})
136
    else:
137
        data = json.dumps({'networks': {'values': networks}})
138

    
139
    return HttpResponse(data, status=200)
140

    
141

    
142
@util.api_method('POST')
143
def create_network(request):
144
    # Normal Response Code: 202
145
    # Error Response Codes: computeFault (400, 500),
146
    #                       serviceUnavailable (503),
147
    #                       unauthorized (401),
148
    #                       badMediaType(415),
149
    #                       badRequest (400),
150
    #                       overLimit (413)
151

    
152
    req = util.get_request_dict(request)
153
    log.debug('create_network %s', req)
154

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

    
168
    if type == 'PUBLIC_ROUTED':
169
        raise Unauthorized('Can not create a public network.')
170

    
171

    
172
    link, mac_prefix = util.network_specs_from_type(type)
173
    if not link:
174
        raise Exception("Can not create network. No connectivity link.")
175

    
176
    network = Network.objects.create(
177
            name=name,
178
            userid=request.user_uniq,
179
            subnet=subnet,
180
            subnet6=subnet6,
181
            gateway=gateway,
182
            gateway6=gateway6,
183
            dhcp=dhcp,
184
            type=type,
185
            link=link,
186
            mac_prefix=mac_prefix,
187
            state='PENDING')
188

    
189
    backend.create_network(network)
190

    
191
    networkdict = network_to_dict(network, request.user_uniq)
192
    return render_network(request, networkdict, status=202)
193

    
194

    
195
@util.api_method('GET')
196
def get_network_details(request, network_id):
197
    # Normal Response Codes: 200, 203
198
    # Error Response Codes: computeFault (400, 500),
199
    #                       serviceUnavailable (503),
200
    #                       unauthorized (401),
201
    #                       badRequest (400),
202
    #                       itemNotFound (404),
203
    #                       overLimit (413)
204

    
205
    log.debug('get_network_details %s', network_id)
206
    net = util.get_network(network_id, request.user_uniq)
207
    netdict = network_to_dict(net, request.user_uniq)
208
    return render_network(request, netdict)
209

    
210

    
211
@util.api_method('PUT')
212
def update_network_name(request, network_id):
213
    # Normal Response Code: 204
214
    # Error Response Codes: computeFault (400, 500),
215
    #                       serviceUnavailable (503),
216
    #                       unauthorized (401),
217
    #                       badRequest (400),
218
    #                       badMediaType(415),
219
    #                       itemNotFound (404),
220
    #                       overLimit (413)
221

    
222
    req = util.get_request_dict(request)
223
    log.debug('update_network_name %s', network_id)
224

    
225
    try:
226
        name = req['network']['name']
227
    except (TypeError, KeyError):
228
        raise BadRequest('Malformed request.')
229

    
230
    net = util.get_network(network_id, request.user_uniq)
231
    if net.public:
232
        raise Unauthorized('Can not rename the public network.')
233
    net.name = name
234
    net.save()
235
    return HttpResponse(status=204)
236

    
237

    
238
@util.api_method('DELETE')
239
def delete_network(request, network_id):
240
    # Normal Response Code: 204
241
    # Error Response Codes: computeFault (400, 500),
242
    #                       serviceUnavailable (503),
243
    #                       unauthorized (401),
244
    #                       itemNotFound (404),
245
    #                       unauthorized (401),
246
    #                       overLimit (413)
247

    
248
    log.debug('delete_network %s', network_id)
249
    net = util.get_network(network_id, request.user_uniq)
250
    if net.public:
251
        raise Unauthorized('Can not delete the public network.')
252

    
253
    if net.machines.all():  # Nics attached on network
254
        raise NetworkInUse('Machines are connected to network.')
255

    
256
    net.action = 'DESTROY'
257
    net.save()
258

    
259
    backend.delete_network(net)
260
    return HttpResponse(status=204)
261

    
262

    
263
@util.api_method('POST')
264
def network_action(request, network_id):
265
    req = util.get_request_dict(request)
266
    log.debug('network_action %s %s', network_id, req)
267
    if len(req) != 1:
268
        raise BadRequest('Malformed request.')
269

    
270
    net = util.get_network(network_id, request.user_uniq)
271
    if net.public:
272
        raise Unauthorized('Can not modify the public network.')
273

    
274
    key = req.keys()[0]
275
    val = req[key]
276

    
277
    try:
278
        assert isinstance(val, dict)
279
        return network_actions[key](request, net, req[key])
280
    except KeyError:
281
        raise BadRequest('Unknown action.')
282
    except AssertionError:
283
        raise BadRequest('Invalid argument.')