Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.3 kB)

1
# Copyright 2011 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 django.conf.urls.defaults import patterns
35
from django.db.models import Q
36
from django.http import HttpResponse
37
from django.template.loader import render_to_string
38
from django.utils import simplejson as json
39

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

    
48

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

    
51

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

    
59

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

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

    
78

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

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

    
97

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

    
129
    return HttpResponse(data, status=200)
130

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

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

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

    
172
@util.api_method('PUT')
173
def update_network_name(request, network_id):
174
    # Normal Response Code: 204
175
    # Error Response Codes: computeFault (400, 500),
176
    #                       serviceUnavailable (503),
177
    #                       unauthorized (401),
178
    #                       badRequest (400),
179
    #                       badMediaType(415),
180
    #                       itemNotFound (404),
181
    #                       overLimit (413)
182

    
183
    req = util.get_request_dict(request)
184
    log.debug('update_network_name %s', network_id)
185
    
186
    try:
187
        name = req['network']['name']
188
    except (TypeError, KeyError):
189
        raise BadRequest('Malformed request.')
190

    
191
    net = util.get_network(network_id, request.user)
192
    if net.public:
193
        raise Unauthorized('Can not rename the public network.')
194
    net.name = name
195
    net.save()
196
    return HttpResponse(status=204)
197

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

    
215
@util.api_method('POST')
216
def network_action(request, network_id):
217
    req = util.get_request_dict(request)
218
    log.debug('network_action %s %s', network_id, req)
219
    if len(req) != 1:
220
        raise BadRequest('Malformed request.')
221
    
222
    net = util.get_network(network_id, request.user)
223
    if net.public:
224
        raise Unauthorized('Can not modify the public network.')
225
    
226
    key = req.keys()[0]
227
    val = req[key]
228

    
229
    try:
230
        assert isinstance(val, dict)
231
        return network_actions[key](request, net, req[key])
232
    except KeyError:
233
        raise BadRequest('Unknown action.')
234
    except AssertionError:
235
        raise BadRequest('Invalid argument.')