Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / networks.py @ 7a0aa449

History | View | Annotate | Download (11.5 kB)

1 6ef51e9f Giorgos Verigakis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 ce55f211 Kostas Papadimitriou
#
3 adee02b8 Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 adee02b8 Giorgos Verigakis
# without modification, are permitted provided that the following
5 adee02b8 Giorgos Verigakis
# conditions are met:
6 ce55f211 Kostas Papadimitriou
#
7 adee02b8 Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 adee02b8 Giorgos Verigakis
#      disclaimer.
10 ce55f211 Kostas Papadimitriou
#
11 adee02b8 Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 adee02b8 Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 adee02b8 Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 adee02b8 Giorgos Verigakis
#      provided with the distribution.
15 ce55f211 Kostas Papadimitriou
#
16 adee02b8 Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 adee02b8 Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 adee02b8 Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 adee02b8 Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 adee02b8 Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 adee02b8 Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 adee02b8 Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 adee02b8 Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 adee02b8 Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 adee02b8 Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 adee02b8 Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 adee02b8 Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 ce55f211 Kostas Papadimitriou
#
29 adee02b8 Giorgos Verigakis
# The views and conclusions contained in the software and
30 adee02b8 Giorgos Verigakis
# documentation are those of the authors and should not be
31 adee02b8 Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 adee02b8 Giorgos Verigakis
# or implied, of GRNET S.A.
33 adee02b8 Giorgos Verigakis
34 2035039b Giorgos Verigakis
from logging import getLogger
35 2035039b Giorgos Verigakis
36 0269afd6 Giorgos Verigakis
from django.conf.urls.defaults import patterns
37 ce55f211 Kostas Papadimitriou
from django.conf import settings
38 d44c236b Giorgos Verigakis
from django.db.models import Q
39 4dba0480 Christos Stavrakakis
from django.db import transaction
40 0269afd6 Giorgos Verigakis
from django.http import HttpResponse
41 69d6d7dd Giorgos Verigakis
from django.template.loader import render_to_string
42 0269afd6 Giorgos Verigakis
from django.utils import simplejson as json
43 0269afd6 Giorgos Verigakis
44 5061546f Christos Stavrakakis
from synnefo.api import util
45 b36f78fa Giorgos Verigakis
from synnefo.api.actions import network_actions
46 b36f78fa Giorgos Verigakis
from synnefo.api.common import method_not_allowed
47 17c2ed57 Christos Stavrakakis
from synnefo.api.faults import (ServiceUnavailable, BadRequest, Forbidden,
48 6eed1ede Christos Stavrakakis
                                NetworkInUse, OverLimit)
49 03992c72 Christos Stavrakakis
from synnefo.db.models import Network
50 03992c72 Christos Stavrakakis
from synnefo.db.pools import EmptyPool
51 b36f78fa Giorgos Verigakis
from synnefo.logic import backend
52 9e98ba3c Giorgos Verigakis
53 9e98ba3c Giorgos Verigakis
54 9e98ba3c Giorgos Verigakis
log = getLogger('synnefo.api')
55 b36f78fa Giorgos Verigakis
56 0269afd6 Giorgos Verigakis
urlpatterns = patterns('synnefo.api.networks',
57 0269afd6 Giorgos Verigakis
    (r'^(?:/|.json|.xml)?$', 'demux'),
58 0269afd6 Giorgos Verigakis
    (r'^/detail(?:.json|.xml)?$', 'list_networks', {'detail': True}),
59 f4fe8796 Giorgos Verigakis
    (r'^/(\w+)(?:.json|.xml)?$', 'network_demux'),
60 f4fe8796 Giorgos Verigakis
    (r'^/(\w+)/action(?:.json|.xml)?$', 'network_action'),
61 0269afd6 Giorgos Verigakis
)
62 0269afd6 Giorgos Verigakis
63 0269afd6 Giorgos Verigakis
64 0269afd6 Giorgos Verigakis
def demux(request):
65 0269afd6 Giorgos Verigakis
    if request.method == 'GET':
66 0269afd6 Giorgos Verigakis
        return list_networks(request)
67 0269afd6 Giorgos Verigakis
    elif request.method == 'POST':
68 0269afd6 Giorgos Verigakis
        return create_network(request)
69 0269afd6 Giorgos Verigakis
    else:
70 0269afd6 Giorgos Verigakis
        return method_not_allowed(request)
71 0269afd6 Giorgos Verigakis
72 6ef51e9f Giorgos Verigakis
73 b19653d1 Giorgos Verigakis
def network_demux(request, network_id):
74 0269afd6 Giorgos Verigakis
    if request.method == 'GET':
75 b19653d1 Giorgos Verigakis
        return get_network_details(request, network_id)
76 0269afd6 Giorgos Verigakis
    elif request.method == 'PUT':
77 b19653d1 Giorgos Verigakis
        return update_network_name(request, network_id)
78 0269afd6 Giorgos Verigakis
    elif request.method == 'DELETE':
79 b19653d1 Giorgos Verigakis
        return delete_network(request, network_id)
80 0269afd6 Giorgos Verigakis
    else:
81 0269afd6 Giorgos Verigakis
        return method_not_allowed(request)
82 0269afd6 Giorgos Verigakis
83 0269afd6 Giorgos Verigakis
84 6ef51e9f Giorgos Verigakis
def network_to_dict(network, user_id, detail=True):
85 7fede91e Christos Stavrakakis
    d = {'id': str(network.id), 'name': network.name}
86 0269afd6 Giorgos Verigakis
    if detail:
87 08b079e2 Stavros Sachtouris
        d['cidr'] = network.subnet
88 cc3f266e Christos Stavrakakis
        d['cidr6'] = network.subnet6
89 08b079e2 Stavros Sachtouris
        d['gateway'] = network.gateway
90 cc3f266e Christos Stavrakakis
        d['gateway6'] = network.gateway6
91 08b079e2 Stavros Sachtouris
        d['dhcp'] = network.dhcp
92 08b079e2 Stavros Sachtouris
        d['type'] = network.type
93 b36f78fa Giorgos Verigakis
        d['updated'] = util.isoformat(network.updated)
94 b36f78fa Giorgos Verigakis
        d['created'] = util.isoformat(network.created)
95 d44c236b Giorgos Verigakis
        d['status'] = network.state
96 7fede91e Christos Stavrakakis
        d['public'] = network.public
97 08b079e2 Stavros Sachtouris
98 7fede91e Christos Stavrakakis
        attachments = [util.construct_nic_id(nic)
99 cd2b0bf5 Christos Stavrakakis
                       for nic in network.nics.filter(machine__userid=user_id)\
100 cd2b0bf5 Christos Stavrakakis
                                              .order_by('machine')]
101 7fede91e Christos Stavrakakis
        d['attachments'] = {'values': attachments}
102 0269afd6 Giorgos Verigakis
    return d
103 0269afd6 Giorgos Verigakis
104 6ef51e9f Giorgos Verigakis
105 0269afd6 Giorgos Verigakis
def render_network(request, networkdict, status=200):
106 0269afd6 Giorgos Verigakis
    if request.serialization == 'xml':
107 0269afd6 Giorgos Verigakis
        data = render_to_string('network.xml', {'network': networkdict})
108 0269afd6 Giorgos Verigakis
    else:
109 0269afd6 Giorgos Verigakis
        data = json.dumps({'network': networkdict})
110 0269afd6 Giorgos Verigakis
    return HttpResponse(data, status=status)
111 0269afd6 Giorgos Verigakis
112 0269afd6 Giorgos Verigakis
113 b36f78fa Giorgos Verigakis
@util.api_method('GET')
114 0269afd6 Giorgos Verigakis
def list_networks(request, detail=False):
115 0269afd6 Giorgos Verigakis
    # Normal Response Codes: 200, 203
116 0269afd6 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
117 0269afd6 Giorgos Verigakis
    #                       serviceUnavailable (503),
118 0269afd6 Giorgos Verigakis
    #                       unauthorized (401),
119 0269afd6 Giorgos Verigakis
    #                       badRequest (400),
120 0269afd6 Giorgos Verigakis
    #                       overLimit (413)
121 ce55f211 Kostas Papadimitriou
122 0c37a721 Christos Stavrakakis
    log.debug('list_networks detail=%s', detail)
123 b36f78fa Giorgos Verigakis
    since = util.isoparse(request.GET.get('changes-since'))
124 8b2515a9 Christos Stavrakakis
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
125 3878553d Kostas Papadimitriou
                                           Q(public=True))
126 ce55f211 Kostas Papadimitriou
127 0269afd6 Giorgos Verigakis
    if since:
128 d44c236b Giorgos Verigakis
        user_networks = user_networks.filter(updated__gte=since)
129 0269afd6 Giorgos Verigakis
        if not user_networks:
130 0269afd6 Giorgos Verigakis
            return HttpResponse(status=304)
131 3878553d Kostas Papadimitriou
    else:
132 3878553d Kostas Papadimitriou
        user_networks = user_networks.filter(deleted=False)
133 ce55f211 Kostas Papadimitriou
134 4b3b8688 Giorgos Verigakis
    networks = [network_to_dict(network, request.user_uniq, detail)
135 cd2b0bf5 Christos Stavrakakis
                for network in user_networks.order_by('id')]
136 ce55f211 Kostas Papadimitriou
137 0269afd6 Giorgos Verigakis
    if request.serialization == 'xml':
138 b36f78fa Giorgos Verigakis
        data = render_to_string('list_networks.xml', {
139 b36f78fa Giorgos Verigakis
            'networks': networks,
140 b36f78fa Giorgos Verigakis
            'detail': detail})
141 0269afd6 Giorgos Verigakis
    else:
142 0269afd6 Giorgos Verigakis
        data = json.dumps({'networks': {'values': networks}})
143 aa197ee4 Vangelis Koukis
144 0269afd6 Giorgos Verigakis
    return HttpResponse(data, status=200)
145 0269afd6 Giorgos Verigakis
146 6ef51e9f Giorgos Verigakis
147 b36f78fa Giorgos Verigakis
@util.api_method('POST')
148 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
149 0269afd6 Giorgos Verigakis
def create_network(request):
150 0269afd6 Giorgos Verigakis
    # Normal Response Code: 202
151 0269afd6 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
152 0269afd6 Giorgos Verigakis
    #                       serviceUnavailable (503),
153 0269afd6 Giorgos Verigakis
    #                       unauthorized (401),
154 13b954b0 Giorgos Verigakis
    #                       badMediaType(415),
155 0269afd6 Giorgos Verigakis
    #                       badRequest (400),
156 17c2ed57 Christos Stavrakakis
    #                       forbidden (403)
157 0269afd6 Giorgos Verigakis
    #                       overLimit (413)
158 aa197ee4 Vangelis Koukis
159 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
160 bf5c82dc Christos Stavrakakis
    log.info('create_network %s', req)
161 ce55f211 Kostas Papadimitriou
162 0269afd6 Giorgos Verigakis
    try:
163 0269afd6 Giorgos Verigakis
        d = req['network']
164 0269afd6 Giorgos Verigakis
        name = d['name']
165 5061546f Christos Stavrakakis
        # TODO: Fix this temp values:
166 b9a0b3d5 Stavros Sachtouris
        subnet = d.get('cidr', '192.168.1.0/24')
167 cc3f266e Christos Stavrakakis
        subnet6 = d.get('cidr6', None)
168 5061546f Christos Stavrakakis
        gateway = d.get('gateway', None)
169 cc3f266e Christos Stavrakakis
        gateway6 = d.get('gateway6', None)
170 af6a3bc5 Christos Stavrakakis
        net_type = d.get('type', 'PRIVATE_MAC_FILTERED')
171 d1b1e683 Dimitris Aragiorgis
        public = d.get('public', False)
172 5061546f Christos Stavrakakis
        dhcp = d.get('dhcp', True)
173 0269afd6 Giorgos Verigakis
    except (KeyError, ValueError):
174 0269afd6 Giorgos Verigakis
        raise BadRequest('Malformed request.')
175 ce55f211 Kostas Papadimitriou
176 d1b1e683 Dimitris Aragiorgis
    if public:
177 17c2ed57 Christos Stavrakakis
        raise Forbidden('Can not create a public network.')
178 87994836 Christos Stavrakakis
179 17600d55 Christos Stavrakakis
    if net_type not in ['PUBLIC_ROUTED', 'PRIVATE_MAC_FILTERED',
180 17600d55 Christos Stavrakakis
                        'PRIVATE_PHYSICAL_VLAN', 'CUSTOM_ROUTED',
181 17600d55 Christos Stavrakakis
                        'CUSTOM_BRIDGED']:
182 17600d55 Christos Stavrakakis
        raise BadRequest("Invalid network type: %s", net_type)
183 17600d55 Christos Stavrakakis
    if net_type not in settings.ENABLED_NETWORKS:
184 17600d55 Christos Stavrakakis
        raise Forbidden("Can not create %s network" % net_type)
185 17600d55 Christos Stavrakakis
186 b10aa469 Christos Stavrakakis
    networks_user_limit = \
187 b10aa469 Christos Stavrakakis
        settings.NETWORKS_USER_QUOTA.get(request.user_uniq,
188 b10aa469 Christos Stavrakakis
                                         settings.MAX_NETWORKS_PER_USER)
189 b10aa469 Christos Stavrakakis
190 f91dee69 Christos Stavrakakis
    user_networks = len(Network.objects.filter(userid=request.user_uniq,
191 f91dee69 Christos Stavrakakis
                                               deleted=False))
192 3e316e90 Christos Stavrakakis
193 b10aa469 Christos Stavrakakis
    if user_networks >= networks_user_limit:
194 f4a66897 Christos Stavrakakis
        raise OverLimit('Network count limit exceeded for your account.')
195 f4a66897 Christos Stavrakakis
196 6eed1ede Christos Stavrakakis
    cidr_block = int(subnet.split('/')[1])
197 b14725eb Christos Stavrakakis
    if not util.validate_network_size(cidr_block):
198 b14725eb Christos Stavrakakis
        raise OverLimit("Unsupported network size.")
199 6dd70a5c Christos Stavrakakis
200 3165f027 Christos Stavrakakis
    try:
201 af6a3bc5 Christos Stavrakakis
        link, mac_prefix = util.net_resources(net_type)
202 3165f027 Christos Stavrakakis
        if not link:
203 3165f027 Christos Stavrakakis
            raise Exception("Can not create network. No connectivity link.")
204 3165f027 Christos Stavrakakis
205 3165f027 Christos Stavrakakis
        network = Network.objects.create(
206 3165f027 Christos Stavrakakis
                name=name,
207 3165f027 Christos Stavrakakis
                userid=request.user_uniq,
208 3165f027 Christos Stavrakakis
                subnet=subnet,
209 3165f027 Christos Stavrakakis
                subnet6=subnet6,
210 3165f027 Christos Stavrakakis
                gateway=gateway,
211 3165f027 Christos Stavrakakis
                gateway6=gateway6,
212 3165f027 Christos Stavrakakis
                dhcp=dhcp,
213 af6a3bc5 Christos Stavrakakis
                type=net_type,
214 3165f027 Christos Stavrakakis
                link=link,
215 af6a3bc5 Christos Stavrakakis
                mac_prefix=mac_prefix,
216 1cf6ac86 Christos Stavrakakis
                action='CREATE',
217 3165f027 Christos Stavrakakis
                state='PENDING')
218 03992c72 Christos Stavrakakis
    except EmptyPool:
219 5e1d07d4 Christos Stavrakakis
        log.error("Failed to allocate resources for network of type: %s",
220 5e1d07d4 Christos Stavrakakis
                  net_type)
221 5e1d07d4 Christos Stavrakakis
        raise ServiceUnavailable("Failed to allocate resources for network")
222 5061546f Christos Stavrakakis
223 7cfbbf32 Christos Stavrakakis
    # Create BackendNetwork entries for each Backend
224 7cfbbf32 Christos Stavrakakis
    network.create_backend_network()
225 7cfbbf32 Christos Stavrakakis
226 7cfbbf32 Christos Stavrakakis
    # Create the network in the actual backends
227 87994836 Christos Stavrakakis
    backend.create_network(network)
228 ce55f211 Kostas Papadimitriou
229 4b3b8688 Giorgos Verigakis
    networkdict = network_to_dict(network, request.user_uniq)
230 0269afd6 Giorgos Verigakis
    return render_network(request, networkdict, status=202)
231 0269afd6 Giorgos Verigakis
232 6ef51e9f Giorgos Verigakis
233 b36f78fa Giorgos Verigakis
@util.api_method('GET')
234 b19653d1 Giorgos Verigakis
def get_network_details(request, network_id):
235 0269afd6 Giorgos Verigakis
    # Normal Response Codes: 200, 203
236 13b954b0 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
237 13b954b0 Giorgos Verigakis
    #                       serviceUnavailable (503),
238 13b954b0 Giorgos Verigakis
    #                       unauthorized (401),
239 13b954b0 Giorgos Verigakis
    #                       badRequest (400),
240 13b954b0 Giorgos Verigakis
    #                       itemNotFound (404),
241 13b954b0 Giorgos Verigakis
    #                       overLimit (413)
242 ce55f211 Kostas Papadimitriou
243 0c37a721 Christos Stavrakakis
    log.debug('get_network_details %s', network_id)
244 4b3b8688 Giorgos Verigakis
    net = util.get_network(network_id, request.user_uniq)
245 4b3b8688 Giorgos Verigakis
    netdict = network_to_dict(net, request.user_uniq)
246 0269afd6 Giorgos Verigakis
    return render_network(request, netdict)
247 0269afd6 Giorgos Verigakis
248 6ef51e9f Giorgos Verigakis
249 b36f78fa Giorgos Verigakis
@util.api_method('PUT')
250 b19653d1 Giorgos Verigakis
def update_network_name(request, network_id):
251 0269afd6 Giorgos Verigakis
    # Normal Response Code: 204
252 13b954b0 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
253 13b954b0 Giorgos Verigakis
    #                       serviceUnavailable (503),
254 13b954b0 Giorgos Verigakis
    #                       unauthorized (401),
255 13b954b0 Giorgos Verigakis
    #                       badRequest (400),
256 17c2ed57 Christos Stavrakakis
    #                       forbidden (403)
257 13b954b0 Giorgos Verigakis
    #                       badMediaType(415),
258 13b954b0 Giorgos Verigakis
    #                       itemNotFound (404),
259 13b954b0 Giorgos Verigakis
    #                       overLimit (413)
260 aa197ee4 Vangelis Koukis
261 b36f78fa Giorgos Verigakis
    req = util.get_request_dict(request)
262 bf5c82dc Christos Stavrakakis
    log.info('update_network_name %s', network_id)
263 ce55f211 Kostas Papadimitriou
264 0269afd6 Giorgos Verigakis
    try:
265 0269afd6 Giorgos Verigakis
        name = req['network']['name']
266 0269afd6 Giorgos Verigakis
    except (TypeError, KeyError):
267 0269afd6 Giorgos Verigakis
        raise BadRequest('Malformed request.')
268 0269afd6 Giorgos Verigakis
269 4b3b8688 Giorgos Verigakis
    net = util.get_network(network_id, request.user_uniq)
270 207b70d5 Giorgos Verigakis
    if net.public:
271 17c2ed57 Christos Stavrakakis
        raise Forbidden('Can not rename the public network.')
272 22a9959d Christos Stavrakakis
    if net.deleted:
273 22a9959d Christos Stavrakakis
        raise Network.DeletedError
274 0269afd6 Giorgos Verigakis
    net.name = name
275 0269afd6 Giorgos Verigakis
    net.save()
276 0269afd6 Giorgos Verigakis
    return HttpResponse(status=204)
277 0269afd6 Giorgos Verigakis
278 6ef51e9f Giorgos Verigakis
279 b36f78fa Giorgos Verigakis
@util.api_method('DELETE')
280 4dba0480 Christos Stavrakakis
@transaction.commit_on_success
281 b19653d1 Giorgos Verigakis
def delete_network(request, network_id):
282 0269afd6 Giorgos Verigakis
    # Normal Response Code: 204
283 13b954b0 Giorgos Verigakis
    # Error Response Codes: computeFault (400, 500),
284 13b954b0 Giorgos Verigakis
    #                       serviceUnavailable (503),
285 13b954b0 Giorgos Verigakis
    #                       unauthorized (401),
286 17c2ed57 Christos Stavrakakis
    #                       forbidden (403)
287 13b954b0 Giorgos Verigakis
    #                       itemNotFound (404),
288 13b954b0 Giorgos Verigakis
    #                       overLimit (413)
289 ce55f211 Kostas Papadimitriou
290 bf5c82dc Christos Stavrakakis
    log.info('delete_network %s', network_id)
291 d2e73c0c Christos Stavrakakis
    net = util.get_network(network_id, request.user_uniq, for_update=True)
292 207b70d5 Giorgos Verigakis
    if net.public:
293 17c2ed57 Christos Stavrakakis
        raise Forbidden('Can not delete the public network.')
294 5061546f Christos Stavrakakis
295 22a9959d Christos Stavrakakis
    if net.deleted:
296 22a9959d Christos Stavrakakis
        raise Network.DeletedError
297 22a9959d Christos Stavrakakis
298 0196d9a3 Christos Stavrakakis
    if net.machines.all():  # Nics attached on network
299 0196d9a3 Christos Stavrakakis
        raise NetworkInUse('Machines are connected to network.')
300 0196d9a3 Christos Stavrakakis
301 22a9959d Christos Stavrakakis
302 5061546f Christos Stavrakakis
    net.action = 'DESTROY'
303 5061546f Christos Stavrakakis
    net.save()
304 5061546f Christos Stavrakakis
305 64938cb0 Giorgos Verigakis
    backend.delete_network(net)
306 0269afd6 Giorgos Verigakis
    return HttpResponse(status=204)
307 0269afd6 Giorgos Verigakis
308 6ef51e9f Giorgos Verigakis
309 b36f78fa Giorgos Verigakis
@util.api_method('POST')
310 b19653d1 Giorgos Verigakis
def network_action(request, network_id):
311 9e98ba3c Giorgos Verigakis
    req = util.get_request_dict(request)
312 0c37a721 Christos Stavrakakis
    log.debug('network_action %s %s', network_id, req)
313 9e98ba3c Giorgos Verigakis
    if len(req) != 1:
314 9e98ba3c Giorgos Verigakis
        raise BadRequest('Malformed request.')
315 ce55f211 Kostas Papadimitriou
316 4b3b8688 Giorgos Verigakis
    net = util.get_network(network_id, request.user_uniq)
317 207b70d5 Giorgos Verigakis
    if net.public:
318 17c2ed57 Christos Stavrakakis
        raise Forbidden('Can not modify the public network.')
319 22a9959d Christos Stavrakakis
    if net.deleted:
320 22a9959d Christos Stavrakakis
        raise Network.DeletedError
321 ce55f211 Kostas Papadimitriou
322 0269afd6 Giorgos Verigakis
    try:
323 e221ade2 Christos Stavrakakis
        key = req.keys()[0]
324 e221ade2 Christos Stavrakakis
        val = req[key]
325 0269afd6 Giorgos Verigakis
        assert isinstance(val, dict)
326 0269afd6 Giorgos Verigakis
        return network_actions[key](request, net, req[key])
327 0269afd6 Giorgos Verigakis
    except KeyError:
328 0269afd6 Giorgos Verigakis
        raise BadRequest('Unknown action.')
329 0269afd6 Giorgos Verigakis
    except AssertionError:
330 0269afd6 Giorgos Verigakis
        raise BadRequest('Invalid argument.')