Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.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.db.models import Q
38
from django.http import HttpResponse
39
from django.template.loader import render_to_string
40
from django.utils import simplejson as json
41

    
42
from synnefo.api import util
43
from synnefo.api.actions import network_actions
44
from synnefo.api.common import method_not_allowed
45
from synnefo.api.faults import BadRequest, OverLimit, Unauthorized
46
from synnefo.db.models import Network
47
from synnefo.logic import backend
48

    
49

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

    
52

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

    
60

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

    
69

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

    
80

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

    
92

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

    
100

    
101
@util.api_method('GET')
102
def list_networks(request, detail=False):
103
    # Normal Response Codes: 200, 203
104
    # Error Response Codes: computeFault (400, 500),
105
    #                       serviceUnavailable (503),
106
    #                       unauthorized (401),
107
    #                       badRequest (400),
108
    #                       overLimit (413)
109
    
110
    log.debug('list_networks detail=%s', detail)
111
    since = util.isoparse(request.GET.get('changes-since'))
112
    user_networks = Network.objects.filter(
113
                                Q(userid=request.user_uniq) | Q(public=True))
114
    
115
    if since:
116
        user_networks = user_networks.filter(updated__gte=since)
117
        if not user_networks:
118
            return HttpResponse(status=304)
119
    else:
120
        user_networks = user_networks.filter(state='ACTIVE')
121
    
122
    networks = [network_to_dict(network, request.user_uniq, detail)
123
                for network in user_networks]
124
    
125
    if request.serialization == 'xml':
126
        data = render_to_string('list_networks.xml', {
127
            'networks': networks,
128
            'detail': detail})
129
    else:
130
        data = json.dumps({'networks': {'values': networks}})
131

    
132
    return HttpResponse(data, status=200)
133

    
134

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

    
145
    req = util.get_request_dict(request)
146
    log.debug('create_network %s', req)
147
    
148
    try:
149
        d = req['network']
150
        name = d['name']
151
    except (KeyError, ValueError):
152
        raise BadRequest('Malformed request.')
153
    
154
    network = backend.create_network(name, request.user_uniq)
155
    if not network:
156
        raise OverLimit('Network count limit exceeded for your account.')
157
    
158
    networkdict = network_to_dict(network, request.user_uniq)
159
    return render_network(request, networkdict, status=202)
160

    
161

    
162
@util.api_method('GET')
163
def get_network_details(request, network_id):
164
    # Normal Response Codes: 200, 203
165
    # Error Response Codes: computeFault (400, 500),
166
    #                       serviceUnavailable (503),
167
    #                       unauthorized (401),
168
    #                       badRequest (400),
169
    #                       itemNotFound (404),
170
    #                       overLimit (413)
171
    
172
    log.debug('get_network_details %s', network_id)
173
    net = util.get_network(network_id, request.user_uniq)
174
    netdict = network_to_dict(net, request.user_uniq)
175
    return render_network(request, netdict)
176

    
177

    
178
@util.api_method('PUT')
179
def update_network_name(request, network_id):
180
    # Normal Response Code: 204
181
    # Error Response Codes: computeFault (400, 500),
182
    #                       serviceUnavailable (503),
183
    #                       unauthorized (401),
184
    #                       badRequest (400),
185
    #                       badMediaType(415),
186
    #                       itemNotFound (404),
187
    #                       overLimit (413)
188

    
189
    req = util.get_request_dict(request)
190
    log.debug('update_network_name %s', network_id)
191
    
192
    try:
193
        name = req['network']['name']
194
    except (TypeError, KeyError):
195
        raise BadRequest('Malformed request.')
196

    
197
    net = util.get_network(network_id, request.user_uniq)
198
    if net.public:
199
        raise Unauthorized('Can not rename the public network.')
200
    net.name = name
201
    net.save()
202
    return HttpResponse(status=204)
203

    
204

    
205
@util.api_method('DELETE')
206
def delete_network(request, network_id):
207
    # Normal Response Code: 204
208
    # Error Response Codes: computeFault (400, 500),
209
    #                       serviceUnavailable (503),
210
    #                       unauthorized (401),
211
    #                       itemNotFound (404),
212
    #                       unauthorized (401),
213
    #                       overLimit (413)
214
    
215
    log.debug('delete_network %s', network_id)
216
    net = util.get_network(network_id, request.user_uniq)
217
    if net.public:
218
        raise Unauthorized('Can not delete the public network.')
219
    backend.delete_network(net)
220
    return HttpResponse(status=204)
221

    
222

    
223
@util.api_method('POST')
224
def network_action(request, network_id):
225
    req = util.get_request_dict(request)
226
    log.debug('network_action %s %s', network_id, req)
227
    if len(req) != 1:
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 modify the public network.')
233
    
234
    key = req.keys()[0]
235
    val = req[key]
236

    
237
    try:
238
        assert isinstance(val, dict)
239
        return network_actions[key](request, net, req[key])
240
    except KeyError:
241
        raise BadRequest('Unknown action.')
242
    except AssertionError:
243
        raise BadRequest('Invalid argument.')