Revision ece5581b snf-cyclades-app/synnefo/api/networks.py

b/snf-cyclades-app/synnefo/api/networks.py
1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
1
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 2
#
3 3
# Redistribution and use in source and binary forms, with or
4 4
# without modification, are permitted provided that the following
......
30 30
# documentation are those of the authors and should not be
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33

  
34

  
35 33
from django.conf import settings
36 34
from django.conf.urls.defaults import patterns
37
from django.db import transaction
38 35
from django.db.models import Q
39 36
from django.http import HttpResponse
40 37
from django.template.loader import render_to_string
......
44 41
from snf_django.lib.api import faults, utils
45 42
from synnefo.api import util
46 43
from synnefo.api.servers import network_actions
47
from synnefo import quotas
48 44
from synnefo.db.models import Network
49
from synnefo.db.utils import validate_mac
50
from synnefo.db.pools import EmptyPool
51
from synnefo.logic import backend
45
from synnefo.logic import networks
52 46

  
53 47

  
54 48
from logging import getLogger
......
59 53
    (r'^(?:/|.json|.xml)?$', 'demux'),
60 54
    (r'^/detail(?:.json|.xml)?$', 'list_networks', {'detail': True}),
61 55
    (r'^/(\w+)(?:.json|.xml)?$', 'network_demux'),
62
    (r'^/(\w+)/action(?:.json|.xml)?$', 'network_action'),
56
    (r'^/(\w+)/action(?:.json|.xml)?$', 'demux_network_action'),
63 57
)
64 58

  
65 59

  
......
137 131
    else:
138 132
        user_networks = user_networks.filter(deleted=False)
139 133

  
140
    networks = [network_to_dict(network, request.user_uniq, detail)
141
                for network in user_networks.order_by('id')]
134
    networks_dict = [network_to_dict(network, request.user_uniq, detail)
135
                     for network in user_networks.order_by('id')]
142 136

  
143 137
    if request.serialization == 'xml':
144 138
        data = render_to_string('list_networks.xml', {
145
            'networks': networks,
139
            'networks': networks_dict,
146 140
            'detail': detail})
147 141
    else:
148
        data = json.dumps({'networks': networks})
142
        data = json.dumps({'networks': networks_dict})
149 143

  
150 144
    return HttpResponse(data, status=200)
151 145

  
152 146

  
153 147
@api.api_method(http_method='POST', user_required=True, logger=log)
154
@transaction.commit_manually
155 148
def create_network(request):
156 149
    # Normal Response Code: 202
157 150
    # Error Response Codes: computeFault (400, 500),
......
162 155
    #                       forbidden (403)
163 156
    #                       overLimit (413)
164 157

  
158
    req = utils.get_request_dict(request)
159
    log.info('create_network %s', req)
160
    user_id = request.user_uniq
165 161
    try:
166
        req = utils.get_request_dict(request)
167
        log.info('create_network %s', req)
168

  
169
        user_id = request.user_uniq
170
        try:
171
            d = req['network']
172
            name = d['name']
173
        except KeyError:
174
            raise faults.BadRequest("Malformed request")
175

  
176
        # Get and validate flavor. Flavors are still exposed as 'type' in the
177
        # API.
178
        flavor = d.get("type", None)
179
        if flavor is None:
180
            raise faults.BadRequest("Missing request parameter 'type'")
181
        elif flavor not in Network.FLAVORS.keys():
182
            raise faults.BadRequest("Invalid network type '%s'" % flavor)
183
        elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
162
        d = req['network']
163
        name = d['name']
164
    except KeyError:
165
        raise faults.BadRequest("Malformed request")
166

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

  
187
        public = d.get("public", False)
188
        if public:
189
            raise faults.Forbidden("Can not create a public network.")
190

  
191
        dhcp = d.get('dhcp', True)
192

  
193
        # Get and validate network parameters
194
        subnet = d.get('cidr', '192.168.1.0/24')
195
        subnet6 = d.get('cidr6', None)
196
        gateway = d.get('gateway', None)
197
        gateway6 = d.get('gateway6', None)
198
        # Check that user provided a valid subnet
199
        util.validate_network_params(subnet, gateway, subnet6, gateway6)
200

  
201
        try:
202
            mode, link, mac_prefix, tags = util.values_from_flavor(flavor)
203
            validate_mac(mac_prefix + "0:00:00:00")
204
            network = Network.objects.create(
205
                name=name,
206
                userid=user_id,
207
                subnet=subnet,
208
                subnet6=subnet6,
209
                gateway=gateway,
210
                gateway6=gateway6,
211
                dhcp=dhcp,
212
                flavor=flavor,
213
                mode=mode,
214
                link=link,
215
                mac_prefix=mac_prefix,
216
                tags=tags,
217
                action='CREATE',
218
                state='ACTIVE')
219
        except EmptyPool:
220
            log.error("Failed to allocate resources for network of type: %s",
221
                      flavor)
222
            raise faults.ServiceUnavailable("Failed to allocate network"
223
                                            " resources")
224

  
225
        # Issue commission to Quotaholder and accept it since at the end of
226
        # this transaction the Network object will be created in the DB.
227
        # Note: the following call does a commit!
228
        quotas.issue_and_accept_commission(network)
229
    except:
230
        transaction.rollback()
231
        raise
232
    else:
233
        transaction.commit()
181
    dhcp = d.get('dhcp', True)
182

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

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

  
235 193
    networkdict = network_to_dict(network, request.user_uniq)
236 194
    response = render_network(request, networkdict, status=202)
......
274 232
    except (TypeError, KeyError):
275 233
        raise faults.BadRequest('Malformed request.')
276 234

  
277
    net = util.get_network(network_id, request.user_uniq)
278
    if net.public:
235
    network = util.get_network(network_id, request.user_uniq)
236
    if network.public:
279 237
        raise faults.Forbidden('Can not rename the public network.')
280
    if net.deleted:
281
        raise faults.BadRequest("Network has been deleted.")
282
    net.name = name
283
    net.save()
238
    network = networks.rename(network, name)
284 239
    return HttpResponse(status=204)
285 240

  
286 241

  
287 242
@api.api_method(http_method='DELETE', user_required=True, logger=log)
288
@transaction.commit_on_success
289 243
def delete_network(request, network_id):
290 244
    # Normal Response Code: 204
291 245
    # Error Response Codes: computeFault (400, 500),
......
296 250
    #                       overLimit (413)
297 251

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

  
303
    if net.deleted:
304
        raise faults.BadRequest("Network has been deleted.")
305

  
306
    if net.machines.all():  # Nics attached on network
307
        raise faults.NetworkInUse('Machines are connected to network.')
308

  
309
    if net.floating_ips.filter(deleted=False).exists():
310
        raise faults.NetworkInUse("Network has allocated floating IP addresses")
311

  
312
    net.action = 'DESTROY'
313
    net.save()
314 259

  
315
    backend_networks = net.backend_networks.exclude(operstate="DELETED")
316
    for bnet in backend_networks:
317
        backend.delete_network(net, bnet.backend)
318
    if not backend_networks:
319
        backend.update_network_state(net)
320
    return HttpResponse(status=204)
260
def key_to_action(action):
261
    return action.upper()
321 262

  
322 263

  
323 264
@api.api_method(http_method='POST', user_required=True, logger=log)
324
def network_action(request, network_id):
265
def demux_network_action(request, network_id):
325 266
    req = utils.get_request_dict(request)
326 267
    log.debug('network_action %s %s', network_id, req)
327 268
    if len(req) != 1:
......
333 274
    if net.deleted:
334 275
        raise faults.BadRequest("Network has been deleted.")
335 276

  
336
    try:
337
        key = req.keys()[0]
338
        val = req[key]
339
        assert isinstance(val, dict)
340
        return network_actions[key](request, net, req[key])
341
    except KeyError:
342
        raise faults.BadRequest('Unknown action.')
343
    except AssertionError:
344
        raise faults.BadRequest('Invalid argument.')
277
    action = req.keys()[0]
278
    if key_to_action(action) not in [x[0] for x in Network.ACTIONS]:
279
        raise faults.BadRequest("Action %s not supported." % action)
280
    action_args = req[action]
281
    if not isinstance(action_args, dict):
282
        raise faults.BadRequest("Invalid argument.")
283
    return network_actions[action](request, net, action_args)

Also available in: Unified diff