Revision b4f491c1

/dev/null
1
# Copyright 2011-2013 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
from django.conf import settings
34
from django.conf.urls import patterns
35

  
36
from django.db.models import Q
37
from django.http import HttpResponse
38
from django.template.loader import render_to_string
39
from django.utils import simplejson as json
40

  
41
from snf_django.lib import api
42
from snf_django.lib.api import faults, utils
43
from synnefo.api import util
44
from synnefo.api.servers import network_actions
45
from synnefo.db.models import Network
46
from synnefo.logic import networks
47

  
48

  
49
from logging import getLogger
50
log = getLogger(__name__)
51

  
52
urlpatterns = patterns(
53
    '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)?$', 'demux_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 api.api_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 api.api_method_not_allowed(request)
79

  
80

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

  
98
        attachments = [nic.id
99
                       for nic in network.nics.filter(machine__userid=user_id)
100
                                              .filter(state="ACTIVE")
101
                                              .order_by('machine')]
102
        d['attachments'] = attachments
103
    return d
104

  
105

  
106
def render_network(request, networkdict, status=200):
107
    if request.serialization == 'xml':
108
        data = render_to_string('network.xml', {'network': networkdict})
109
    else:
110
        data = json.dumps({'network': networkdict})
111
    return HttpResponse(data, status=status)
112

  
113

  
114
@api.api_method(http_method='GET', user_required=True, logger=log)
115
def list_networks(request, detail=False):
116
    # Normal Response Codes: 200, 203
117
    # Error Response Codes: computeFault (400, 500),
118
    #                       serviceUnavailable (503),
119
    #                       unauthorized (401),
120
    #                       badRequest (400),
121
    #                       overLimit (413)
122

  
123
    log.debug('list_networks detail=%s', detail)
124
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
125
                                           Q(public=True))
126
    user_networks = utils.filter_modified_since(request, objects=user_networks)
127

  
128
    networks_dict = [network_to_dict(network, request.user_uniq, detail)
129
                     for network in user_networks.order_by('id')]
130

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

  
138
    return HttpResponse(data, status=200)
139

  
140

  
141
@api.api_method(http_method='POST', user_required=True, logger=log)
142
def create_network(request):
143
    # Normal Response Code: 202
144
    # Error Response Codes: computeFault (400, 500),
145
    #                       serviceUnavailable (503),
146
    #                       unauthorized (401),
147
    #                       badMediaType(415),
148
    #                       badRequest (400),
149
    #                       forbidden (403)
150
    #                       overLimit (413)
151

  
152
    req = utils.get_request_dict(request)
153
    log.info('create_network %s', req)
154
    user_id = request.user_uniq
155
    try:
156
        d = req['network']
157
        name = d['name']
158
    except KeyError:
159
        raise faults.BadRequest("Malformed request")
160

  
161
    # Get and validate flavor. Flavors are still exposed as 'type' in the
162
    # API.
163
    flavor = d.get("type", None)
164
    if flavor is None:
165
        raise faults.BadRequest("Missing request parameter 'type'")
166
    elif flavor not in Network.FLAVORS.keys():
167
        raise faults.BadRequest("Invalid network type '%s'" % flavor)
168
    elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
169
        raise faults.Forbidden("Cannot create network of type '%s'" %
170
                               flavor)
171

  
172
    public = d.get("public", False)
173
    if public:
174
        raise faults.Forbidden("Cannot create a public network.")
175

  
176
    dhcp = d.get('dhcp', True)
177

  
178
    # Get and validate network parameters
179
    subnet = d.get('cidr', '192.168.1.0/24')
180
    subnet6 = d.get('cidr6', None)
181
    gateway = d.get('gateway', None)
182
    gateway6 = d.get('gateway6', None)
183

  
184
    network = networks.create(user_id=user_id, name=name, flavor=flavor,
185
                              subnet=subnet, gateway=gateway, subnet6=subnet6,
186
                              gateway6=gateway6, dhcp=dhcp, public=False)
187

  
188
    networkdict = network_to_dict(network, request.user_uniq)
189
    response = render_network(request, networkdict, status=202)
190

  
191
    return response
192

  
193

  
194
@api.api_method(http_method='GET', user_required=True, logger=log)
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
@api.api_method(http_method='PUT', user_required=True, logger=log)
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
    #                       forbidden (403)
218
    #                       badMediaType(415),
219
    #                       itemNotFound (404),
220
    #                       overLimit (413)
221

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

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

  
230
    network = util.get_network(network_id, request.user_uniq)
231
    if network.public:
232
        raise faults.Forbidden('Cannot rename the public network.')
233
    network = networks.rename(network, name)
234
    return HttpResponse(status=204)
235

  
236

  
237
@api.api_method(http_method='DELETE', user_required=True, logger=log)
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
    #                       forbidden (403)
244
    #                       itemNotFound (404),
245
    #                       overLimit (413)
246

  
247
    log.info('delete_network %s', network_id)
248
    network = util.get_network(network_id, request.user_uniq, for_update=True)
249
    if network.public:
250
        raise faults.Forbidden('Cannot delete the public network.')
251
    networks.delete(network)
252
    return HttpResponse(status=204)
253

  
254

  
255
def key_to_action(action):
256
    return action.upper()
257

  
258

  
259
@api.api_method(http_method='POST', user_required=True, logger=log)
260
def demux_network_action(request, network_id):
261
    req = utils.get_request_dict(request)
262
    log.debug('network_action %s %s', network_id, req)
263
    if len(req) != 1:
264
        raise faults.BadRequest('Malformed request.')
265

  
266
    net = util.get_network(network_id, request.user_uniq)
267
    if net.public:
268
        raise faults.Forbidden('Cannot modify the public network.')
269
    if net.deleted:
270
        raise faults.BadRequest("Network has been deleted.")
271

  
272
    action = req.keys()[0]
273
    if key_to_action(action) not in [x[0] for x in Network.ACTIONS]:
274
        raise faults.BadRequest("Action %s not supported." % action)
275
    action_args = req[action]
276
    if not isinstance(action_args, dict):
277
        raise faults.BadRequest("Invalid argument.")
278
    return network_actions[action](request, net, action_args)

Also available in: Unified diff