Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.7 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

    
34
from django.conf import settings
35
from django.conf.urls import patterns
36
from django.http import HttpResponse
37
from django.utils import simplejson as json
38
from django.db import transaction
39
from django.db.models import Q
40
from django.template.loader import render_to_string
41

    
42
from snf_django.lib import api
43
from snf_django.lib.api import utils
44

    
45
from synnefo.api import util
46
from synnefo.db.models import Network
47
from synnefo.logic import networks
48

    
49
from logging import getLogger
50

    
51
log = getLogger(__name__)
52

    
53
urlpatterns = patterns(
54
    'synnefo.api.networks',
55
    (r'^(?:/|.json|.xml)?$', 'demux'),
56
    (r'^/detail(?:.json|.xml)?$', 'list_networks', {'detail': True}),
57
    (r'^/(\w+)(?:/|.json|.xml)?$', 'network_demux'),
58
    (r'^/(\w+)/action(?:/|.json|.xml)?$', 'network_action_demux'),
59
)
60

    
61

    
62
def demux(request):
63
    if request.method == 'GET':
64
        return list_networks(request)
65
    elif request.method == 'POST':
66
        return create_network(request)
67
    else:
68
        return api.api_method_not_allowed(request,
69
                                          allowed_methods=['GET', 'POST'])
70

    
71

    
72
def network_demux(request, network_id):
73

    
74
    if request.method == 'GET':
75
        return get_network_details(request, network_id)
76
    elif request.method == 'DELETE':
77
        return delete_network(request, network_id)
78
    elif request.method == 'PUT':
79
        return update_network(request, network_id)
80
    else:
81
        return api.api_method_not_allowed(request,
82
                                          allowed_methods=['GET',
83
                                                           'PUT',
84
                                                           'DELETE'])
85

    
86

    
87
@api.api_method(http_method='POST', user_required=True, logger=log)
88
def network_action_demux(request, network_id):
89
    req = utils.get_request_dict(request)
90
    network = util.get_network(network_id, request.user_uniq, for_update=True)
91
    action = req.keys()[0]
92
    try:
93
        f = NETWORK_ACTIONS[action]
94
    except KeyError:
95
        raise faults.BadRequest("Action %s not supported." % action)
96
    action_args = req[action]
97
    if not isinstance(action_args, dict):
98
        raise faults.BadRequest("Invalid argument.")
99

    
100
    return f(request, network, action_args)
101

    
102

    
103
@api.api_method(http_method='GET', user_required=True, logger=log)
104
def list_networks(request, detail=True):
105
    log.debug('list_networks detail=%s', detail)
106

    
107
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
108
                                           Q(public=True))\
109
                                   .order_by('id')
110
    if detail:
111
        user_networks = user_networks.prefetch_related("subnets")
112

    
113
    user_networks = api.utils.filter_modified_since(request,
114
                                                    objects=user_networks)
115

    
116
    network_dicts = [network_to_dict(network, detail)
117
                     for network in user_networks]
118

    
119
    if request.serialization == 'xml':
120
        data = render_to_string('list_networks.xml', {
121
            "networks": network_dicts})
122
    else:
123
        data = json.dumps({'networks': network_dicts})
124

    
125
    return HttpResponse(data, status=200)
126

    
127

    
128
@api.api_method(http_method='POST', user_required=True, logger=log)
129
def create_network(request):
130
    userid = request.user_uniq
131
    req = api.utils.get_request_dict(request)
132
    log.info('create_network %s', req)
133

    
134
    network_dict = api.utils.get_attribute(req, "network",
135
                                           attr_type=dict)
136
    flavor = api.utils.get_attribute(network_dict, "type",
137
                                     attr_type=basestring)
138

    
139
    if flavor not in Network.FLAVORS.keys():
140
        raise api.faults.BadRequest("Invalid network type '%s'" % flavor)
141
    if flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
142
        raise api.faults.Forbidden("Cannot create network of type '%s'." %
143
                                   flavor)
144

    
145
    name = api.utils.get_attribute(network_dict, "name", attr_type=basestring,
146
                                   required=False)
147
    if name is None:
148
        name = ""
149

    
150
    project = network_dict.get('project', None)
151
    network = networks.create(userid=userid, name=name, flavor=flavor,
152
                              public=False, project=project)
153
    networkdict = network_to_dict(network, detail=True)
154
    response = render_network(request, networkdict, status=201)
155

    
156
    return response
157

    
158

    
159
@api.api_method(http_method='GET', user_required=True, logger=log)
160
def get_network_details(request, network_id):
161
    log.debug('get_network_details %s', network_id)
162
    network = util.get_network(network_id, request.user_uniq)
163
    return render_network(request, network_to_dict(network, detail=True))
164

    
165

    
166
@api.api_method(http_method='PUT', user_required=True, logger=log)
167
def update_network(request, network_id):
168
    info = api.utils.get_request_dict(request)
169

    
170
    network = api.utils.get_attribute(info, "network", attr_type=dict,
171
                                      required=True)
172
    new_name = api.utils.get_attribute(network, "name", attr_type=basestring)
173

    
174
    network = util.get_network(network_id, request.user_uniq, for_update=True)
175
    if network.public:
176
        raise api.faults.Forbidden("Cannot rename the public network.")
177
    network = networks.rename(network, new_name)
178
    return render_network(request, network_to_dict(network), 200)
179

    
180

    
181
@api.api_method(http_method='DELETE', user_required=True, logger=log)
182
@transaction.commit_on_success
183
def delete_network(request, network_id):
184
    log.info('delete_network %s', network_id)
185
    network = util.get_network(network_id, request.user_uniq, for_update=True)
186
    if network.public:
187
        raise api.faults.Forbidden("Cannot delete the public network.")
188
    networks.delete(network)
189
    return HttpResponse(status=204)
190

    
191

    
192
def network_to_dict(network, detail=True):
193
    d = {'id': str(network.id), 'name': network.name}
194
    d['links'] = util.network_to_links(network.id)
195
    if detail:
196
        # Loop over subnets. Do not perform any extra query because of prefetch
197
        # related!
198
        subnet_ids = []
199
        for subnet in network.subnets.all():
200
            subnet_ids.append(subnet.id)
201

    
202
        state = "SNF:DRAINED" if network.drained else network.state
203
        d['user_id'] = network.userid
204
        d['tenant_id'] = network.userid
205
        d['type'] = network.flavor
206
        d['updated'] = api.utils.isoformat(network.updated)
207
        d['created'] = api.utils.isoformat(network.created)
208
        d['status'] = state
209
        d['public'] = network.public
210
        d['router:external'] = network.external_router
211
        d['admin_state_up'] = True
212
        d['subnets'] = subnet_ids
213
        d['SNF:floating_ip_pool'] = network.floating_ip_pool
214
        d['deleted'] = network.deleted
215
    return d
216

    
217

    
218
@transaction.commit_on_success
219
def reassign_network(request, network, args):
220
    project = args.get("project")
221
    if project is None:
222
        raise faults.BadRequest("Missing 'project' attribute.")
223
    networks.reassign(network, project)
224
    return HttpResponse(status=200)
225

    
226

    
227
NETWORK_ACTIONS = {
228
    "reassign": reassign_network,
229
}
230

    
231

    
232
def render_network(request, networkdict, status=200):
233
    if request.serialization == 'xml':
234
        data = render_to_string('network.xml', {'network': networkdict})
235
    else:
236
        data = json.dumps({'network': networkdict})
237
    return HttpResponse(data, status=status)