Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.3 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.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 snf_django.lib import api
41
from snf_django.lib.api import faults, utils
42
from synnefo.api import util
43
from synnefo.api.servers import network_actions
44
from synnefo.db.models import Network
45
from synnefo.logic import networks
46

    
47

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

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

    
68

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

    
79

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

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

    
104

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

    
112

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

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

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

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

    
137
    return HttpResponse(data, status=200)
138

    
139

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

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

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

    
174
    dhcp = d.get('dhcp', True)
175

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

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

    
186
    networkdict = network_to_dict(network, request.user_uniq)
187
    response = render_network(request, networkdict, status=202)
188

    
189
    return response
190

    
191

    
192
@api.api_method(http_method='GET', user_required=True, logger=log)
193
def get_network_details(request, network_id):
194
    # Normal Response Codes: 200, 203
195
    # Error Response Codes: computeFault (400, 500),
196
    #                       serviceUnavailable (503),
197
    #                       unauthorized (401),
198
    #                       badRequest (400),
199
    #                       itemNotFound (404),
200
    #                       overLimit (413)
201

    
202
    log.debug('get_network_details %s', network_id)
203
    net = util.get_network(network_id, request.user_uniq)
204
    netdict = network_to_dict(net, request.user_uniq)
205
    return render_network(request, netdict)
206

    
207

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

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

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

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

    
234

    
235
@api.api_method(http_method='DELETE', user_required=True, logger=log)
236
def delete_network(request, network_id):
237
    # Normal Response Code: 204
238
    # Error Response Codes: computeFault (400, 500),
239
    #                       serviceUnavailable (503),
240
    #                       unauthorized (401),
241
    #                       forbidden (403)
242
    #                       itemNotFound (404),
243
    #                       overLimit (413)
244

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

    
252

    
253
def key_to_action(action):
254
    return action.upper()
255

    
256

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

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

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