Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.8 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 faults, 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, OverLimit, Unauthorized
47
from synnefo.db.models import Network, NetworkLink
48
from synnefo.logic import backend
49

    
50

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

    
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['updated'] = util.isoformat(network.updated)
87
        d['created'] = util.isoformat(network.created)
88
        d['status'] = network.state
89
        servers = [vm.id for vm in network.machines.filter(userid=user_id)]
90
        d['servers'] = {'values': servers}
91
    return d
92

    
93

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

    
101

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

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

    
116
    if since:
117
        user_networks = user_networks.filter(updated__gte=since)
118
        if not user_networks:
119
            return HttpResponse(status=304)
120
    else:
121
        user_networks = user_networks.filter(state='ACTIVE')
122

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

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

    
133
    return HttpResponse(data, status=200)
134

    
135

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

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

    
149
    try:
150
        d = req['network']
151
        name = d['name']
152
    except (KeyError, ValueError):
153
        raise BadRequest('Malformed request.')
154

    
155
    count = Network.objects.filter(userid=request.user_uniq,
156
                                          state='ACTIVE').count()
157

    
158
    # get user limit
159
    networks_limit_for_user = \
160
        settings.NETWORKS_USER_QUOTA.get(request.user_uniq,
161
                settings.MAX_NETWORKS_PER_USER)
162

    
163
    if count >= networks_limit_for_user:
164
        raise faults.OverLimit("Network count limit exceeded for your account.")
165

    
166
    try:
167
        network = backend.create_network(name, request.user_uniq)
168
    except NetworkLink.NotAvailable:
169
        raise faults.OverLimit('No networks available.')
170

    
171
    networkdict = network_to_dict(network, request.user_uniq)
172
    return render_network(request, networkdict, status=202)
173

    
174

    
175
@util.api_method('GET')
176
def get_network_details(request, network_id):
177
    # Normal Response Codes: 200, 203
178
    # Error Response Codes: computeFault (400, 500),
179
    #                       serviceUnavailable (503),
180
    #                       unauthorized (401),
181
    #                       badRequest (400),
182
    #                       itemNotFound (404),
183
    #                       overLimit (413)
184

    
185
    log.debug('get_network_details %s', network_id)
186
    net = util.get_network(network_id, request.user_uniq)
187
    netdict = network_to_dict(net, request.user_uniq)
188
    return render_network(request, netdict)
189

    
190

    
191
@util.api_method('PUT')
192
def update_network_name(request, network_id):
193
    # Normal Response Code: 204
194
    # Error Response Codes: computeFault (400, 500),
195
    #                       serviceUnavailable (503),
196
    #                       unauthorized (401),
197
    #                       badRequest (400),
198
    #                       badMediaType(415),
199
    #                       itemNotFound (404),
200
    #                       overLimit (413)
201

    
202
    req = util.get_request_dict(request)
203
    log.debug('update_network_name %s', network_id)
204

    
205
    try:
206
        name = req['network']['name']
207
    except (TypeError, KeyError):
208
        raise BadRequest('Malformed request.')
209

    
210
    net = util.get_network(network_id, request.user_uniq)
211
    if net.public:
212
        raise Unauthorized('Can not rename the public network.')
213
    net.name = name
214
    net.save()
215
    return HttpResponse(status=204)
216

    
217

    
218
@util.api_method('DELETE')
219
def delete_network(request, network_id):
220
    # Normal Response Code: 204
221
    # Error Response Codes: computeFault (400, 500),
222
    #                       serviceUnavailable (503),
223
    #                       unauthorized (401),
224
    #                       itemNotFound (404),
225
    #                       unauthorized (401),
226
    #                       overLimit (413)
227

    
228
    log.debug('delete_network %s', network_id)
229
    net = util.get_network(network_id, request.user_uniq)
230
    if net.public:
231
        raise Unauthorized('Can not delete the public network.')
232
    backend.delete_network(net)
233
    return HttpResponse(status=204)
234

    
235

    
236
@util.api_method('POST')
237
def network_action(request, network_id):
238
    req = util.get_request_dict(request)
239
    log.debug('network_action %s %s', network_id, req)
240
    if len(req) != 1:
241
        raise BadRequest('Malformed request.')
242

    
243
    net = util.get_network(network_id, request.user_uniq)
244
    if net.public:
245
        raise Unauthorized('Can not modify the public network.')
246

    
247
    key = req.keys()[0]
248
    val = req[key]
249

    
250
    try:
251
        assert isinstance(val, dict)
252
        return network_actions[key](request, net, req[key])
253
    except KeyError:
254
        raise BadRequest('Unknown action.')
255
    except AssertionError:
256
        raise BadRequest('Invalid argument.')