Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / networks.py @ 04fe9a86

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

    
43
from synnefo import settings
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, OverLimit, Unauthorized
48
from synnefo.db.models import Network, BridgePool, MacPrefixPool
49
from synnefo.logic import backend
50

    
51

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

    
54

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

    
62

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

    
71

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

    
82

    
83
def network_to_dict(network, user_id, detail=True):
84
    network_id = str(network.id) if not network.public else 'public'
85
    d = {'id': network_id, 'name': network.name}
86
    if detail:
87
        d['updated'] = util.isoformat(network.updated)
88
        d['created'] = util.isoformat(network.created)
89
        d['status'] = network.state
90
        servers = [vm.id for vm in network.machines.filter(userid=user_id)]
91
        d['servers'] = {'values': servers}
92
    return d
93

    
94

    
95
def render_network(request, networkdict, status=200):
96
    if request.serialization == 'xml':
97
        data = render_to_string('network.xml', {'network': networkdict})
98
    else:
99
        data = json.dumps({'network': networkdict})
100
    return HttpResponse(data, status=status)
101

    
102

    
103
@util.api_method('GET')
104
def list_networks(request, detail=False):
105
    # Normal Response Codes: 200, 203
106
    # Error Response Codes: computeFault (400, 500),
107
    #                       serviceUnavailable (503),
108
    #                       unauthorized (401),
109
    #                       badRequest (400),
110
    #                       overLimit (413)
111

    
112
    log.debug('list_networks detail=%s', detail)
113
    since = util.isoparse(request.GET.get('changes-since'))
114
    user_networks = Network.objects.filter(
115
                                Q(userid=request.user_uniq) | Q(public=True))
116

    
117
    if since:
118
        user_networks = user_networks.filter(updated__gte=since)
119
        if not user_networks:
120
            return HttpResponse(status=304)
121
    else:
122
        user_networks = user_networks.filter(deleted=False)
123

    
124
    networks = [network_to_dict(network, request.user_uniq, detail)
125
                for network in user_networks]
126

    
127
    if request.serialization == 'xml':
128
        data = render_to_string('list_networks.xml', {
129
            'networks': networks,
130
            'detail': detail})
131
    else:
132
        data = json.dumps({'networks': {'values': networks}})
133

    
134
    return HttpResponse(data, status=200)
135

    
136

    
137
@util.api_method('POST')
138
def create_network(request):
139
    # Normal Response Code: 202
140
    # Error Response Codes: computeFault (400, 500),
141
    #                       serviceUnavailable (503),
142
    #                       unauthorized (401),
143
    #                       badMediaType(415),
144
    #                       badRequest (400),
145
    #                       overLimit (413)
146

    
147
    req = util.get_request_dict(request)
148
    log.debug('create_network %s', req)
149

    
150
    try:
151
        d = req['network']
152
        name = d['name']
153
        # TODO: Fix this temp values:
154
        subnet = d.get('subnet', '192.168.1.0/24')
155
        gateway = d.get('gateway', None)
156
        type = d.get('type', 'PRIVATE_VLAN')
157
        dhcp = d.get('dhcp', True)
158
    except (KeyError, ValueError):
159
        raise BadRequest('Malformed request.')
160

    
161
    link = None
162
    mac_prefix = None
163
    if type == 'PUBLIC_ROUTED':
164
        pass
165
        # raise Exception (user can not create public)
166
    if type == 'PRIVATE_FILTERED':
167
        link = settings.GANETI_PRIVATE_BRIDGE
168
        mac_prefix = MacPrefixPool.get_available().value
169
        state = 'PENDING'
170
    else: # PRIVATE_VLAN
171
        link = BridgePool.get_available().value
172
        # Physical-Vlans are pre-provisioned
173
        state = 'ACTIVE'
174

    
175
    network = Network.objects.create(
176
            name=name,
177
            userid=request.user_uniq,
178
            subnet=subnet,
179
            gateway=gateway,
180
            dhcp=dhcp,
181
            type=type,
182
            link=link,
183
            mac_prefix=mac_prefix,
184
            state=state)
185

    
186
    network = backend.create_network(network)
187
    if not network:
188
        raise OverLimit('Network count limit exceeded for your account.')
189

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

    
193

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

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

    
209

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

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

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

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

    
236

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

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

    
252
    net.action = 'DESTROY'
253
    net.save()
254

    
255
    backend.delete_network(net)
256
    return HttpResponse(status=204)
257

    
258

    
259
@util.api_method('POST')
260
def network_action(request, network_id):
261
    req = util.get_request_dict(request)
262
    log.debug('network_action %s %s', network_id, req)
263
    if len(req) != 1:
264
        raise BadRequest('Malformed request.')
265

    
266
    net = util.get_network(network_id, request.user_uniq)
267
    if net.public:
268
        raise Unauthorized('Can not modify the public network.')
269

    
270
    key = req.keys()[0]
271
    val = req[key]
272

    
273
    try:
274
        assert isinstance(val, dict)
275
        return network_actions[key](request, net, req[key])
276
    except KeyError:
277
        raise BadRequest('Unknown action.')
278
    except AssertionError:
279
        raise BadRequest('Invalid argument.')