Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.5 kB)

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
                                          allowed_methods=['GET', 'POST'])
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 api.api_method_not_allowed(request,
80
                                          allowed_methods=['GET',
81
                                                           'PUT',
82
                                                           'DELETE'])
83

    
84

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

    
102
        attachments = [util.construct_nic_id(nic)
103
                       for nic in network.nics.filter(machine__userid=user_id)
104
                                              .filter(state="ACTIVE")
105
                                              .order_by('machine')]
106
        d['attachments'] = attachments
107
    return d
108

    
109

    
110
def render_network(request, networkdict, status=200):
111
    if request.serialization == 'xml':
112
        data = render_to_string('network.xml', {'network': networkdict})
113
    else:
114
        data = json.dumps({'network': networkdict})
115
    return HttpResponse(data, status=status)
116

    
117

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

    
127
    log.debug('list_networks detail=%s', detail)
128
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
129
                                           Q(public=True))
130
    user_networks = utils.filter_modified_since(request, objects=user_networks)
131

    
132
    networks_dict = [network_to_dict(network, request.user_uniq, detail)
133
                     for network in user_networks.order_by('id')]
134

    
135
    if request.serialization == 'xml':
136
        data = render_to_string('list_networks.xml', {
137
            'networks': networks_dict,
138
            'detail': detail})
139
    else:
140
        data = json.dumps({'networks': networks_dict})
141

    
142
    return HttpResponse(data, status=200)
143

    
144

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

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

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

    
176
    public = d.get("public", False)
177
    if public:
178
        raise faults.Forbidden("Can not create a public network.")
179

    
180
    dhcp = d.get('dhcp', True)
181

    
182
    # Get and validate network parameters
183
    subnet = d.get('cidr', '192.168.1.0/24')
184
    subnet6 = d.get('cidr6', None)
185
    gateway = d.get('gateway', None)
186
    gateway6 = d.get('gateway6', None)
187

    
188
    network = networks.create(user_id=user_id, name=name, flavor=flavor,
189
                              subnet=subnet, gateway=gateway, subnet6=subnet6,
190
                              gateway6=gateway6, dhcp=dhcp, public=False)
191

    
192
    networkdict = network_to_dict(network, request.user_uniq)
193
    response = render_network(request, networkdict, status=202)
194

    
195
    return response
196

    
197

    
198
@api.api_method(http_method='GET', user_required=True, logger=log)
199
def get_network_details(request, network_id):
200
    # Normal Response Codes: 200, 203
201
    # Error Response Codes: computeFault (400, 500),
202
    #                       serviceUnavailable (503),
203
    #                       unauthorized (401),
204
    #                       badRequest (400),
205
    #                       itemNotFound (404),
206
    #                       overLimit (413)
207

    
208
    log.debug('get_network_details %s', network_id)
209
    net = util.get_network(network_id, request.user_uniq)
210
    netdict = network_to_dict(net, request.user_uniq)
211
    return render_network(request, netdict)
212

    
213

    
214
@api.api_method(http_method='PUT', user_required=True, logger=log)
215
def update_network_name(request, network_id):
216
    # Normal Response Code: 204
217
    # Error Response Codes: computeFault (400, 500),
218
    #                       serviceUnavailable (503),
219
    #                       unauthorized (401),
220
    #                       badRequest (400),
221
    #                       forbidden (403)
222
    #                       badMediaType(415),
223
    #                       itemNotFound (404),
224
    #                       overLimit (413)
225

    
226
    req = utils.get_request_dict(request)
227
    log.info('update_network_name %s', network_id)
228

    
229
    try:
230
        name = req['network']['name']
231
    except (TypeError, KeyError):
232
        raise faults.BadRequest('Malformed request.')
233

    
234
    network = util.get_network(network_id, request.user_uniq)
235
    if network.public:
236
        raise faults.Forbidden('Can not rename the public network.')
237
    network = networks.rename(network, name)
238
    return HttpResponse(status=204)
239

    
240

    
241
@api.api_method(http_method='DELETE', user_required=True, logger=log)
242
def delete_network(request, network_id):
243
    # Normal Response Code: 204
244
    # Error Response Codes: computeFault (400, 500),
245
    #                       serviceUnavailable (503),
246
    #                       unauthorized (401),
247
    #                       forbidden (403)
248
    #                       itemNotFound (404),
249
    #                       overLimit (413)
250

    
251
    log.info('delete_network %s', network_id)
252
    network = util.get_network(network_id, request.user_uniq, for_update=True)
253
    if network.public:
254
        raise faults.Forbidden('Can not delete the public network.')
255
    networks.delete(network)
256
    return HttpResponse(status=204)
257

    
258

    
259
def key_to_action(action):
260
    return action.upper()
261

    
262

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

    
270
    net = util.get_network(network_id, request.user_uniq)
271
    if net.public:
272
        raise faults.Forbidden('Can not modify the public network.')
273
    if net.deleted:
274
        raise faults.BadRequest("Network has been deleted.")
275

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