networks.py 7.69 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

34
from django.conf import settings
35
from django.conf.urls import patterns
36 37
from django.http import HttpResponse
from django.utils import simplejson as json
Marios Kogias's avatar
Marios Kogias committed
38 39 40
from django.db import transaction
from django.db.models import Q
from django.template.loader import render_to_string
41 42 43

from snf_django.lib import api

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

48
from logging import getLogger
Marios Kogias's avatar
Marios Kogias committed
49

50
log = getLogger(__name__)
51

Christos Stavrakakis's avatar
Christos Stavrakakis committed
52 53
urlpatterns = patterns(
    'synnefo.api.networks',
54
    (r'^(?:/|.json|.xml)?$', 'demux'),
55
    (r'^/detail(?:.json|.xml)?$', 'list_networks', {'detail': True}),
56 57
    (r'^/(\w+)(?:/|.json|.xml)?$', 'network_demux'))

58 59 60 61 62 63 64

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

68

69
def network_demux(request, network_id):
Marios Kogias's avatar
Marios Kogias committed
70

71
    if request.method == 'GET':
72
        return get_network_details(request, network_id)
73
    elif request.method == 'DELETE':
74
        return delete_network(request, network_id)
Marios Kogias's avatar
Marios Kogias committed
75
    elif request.method == 'PUT':
76
        return update_network(request, network_id)
77
    else:
78 79 80 81
        return api.api_method_not_allowed(request,
                                          allowed_methods=['GET',
                                                           'PUT',
                                                           'DELETE'])
82 83


84
@api.api_method(http_method='GET', user_required=True, logger=log)
85
def list_networks(request, detail=True):
Christos Stavrakakis's avatar
Christos Stavrakakis committed
86
    log.debug('list_networks detail=%s', detail)
Marios Kogias's avatar
Marios Kogias committed
87

88 89
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
                                           Q(public=True))\
90 91 92
                                   .order_by('id')
    if detail:
        user_networks = user_networks.prefetch_related("subnets")
Marios Kogias's avatar
Marios Kogias committed
93

94 95
    user_networks = api.utils.filter_modified_since(request,
                                                    objects=user_networks)
96

97
    network_dicts = [network_to_dict(network, detail)
98
                     for network in user_networks]
99

100
    if request.serialization == 'xml':
101
        data = render_to_string('list_networks.xml', {
102
            "networks": network_dicts})
103
    else:
104
        data = json.dumps({'networks': network_dicts})
105

106 107
    return HttpResponse(data, status=200)

108

109
@api.api_method(http_method='POST', user_required=True, logger=log)
110
def create_network(request):
111 112 113 114
    userid = request.user_uniq
    req = api.utils.get_request_dict(request)
    log.info('create_network %s', req)

115 116 117 118
    network_dict = api.utils.get_attribute(req, "network",
                                           attr_type=dict)
    flavor = api.utils.get_attribute(network_dict, "type",
                                     attr_type=basestring)
119 120 121 122

    if flavor not in Network.FLAVORS.keys():
        raise api.faults.BadRequest("Invalid network type '%s'" % flavor)
    if flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
123
        raise api.faults.Forbidden("Cannot create network of type '%s'." %
124 125
                                   flavor)

126 127
    name = api.utils.get_attribute(network_dict, "name", attr_type=basestring,
                                   required=False)
128 129 130
    if name is None:
        name = ""

131
    project = network_dict.get('project', None)
132
    network = networks.create(userid=userid, name=name, flavor=flavor,
133
                              public=False, project=project)
134
    networkdict = network_to_dict(network, detail=True)
Marios Kogias's avatar
Marios Kogias committed
135
    response = render_network(request, networkdict, status=201)
136 137

    return response
138

139

140
@api.api_method(http_method='GET', user_required=True, logger=log)
141
def get_network_details(request, network_id):
Christos Stavrakakis's avatar
Christos Stavrakakis committed
142
    log.debug('get_network_details %s', network_id)
143 144
    network = util.get_network(network_id, request.user_uniq)
    return render_network(request, network_to_dict(network, detail=True))
145

146 147 148 149 150

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

151 152 153
    network = api.utils.get_attribute(info, "network", attr_type=dict,
                                      required=True)
    new_name = api.utils.get_attribute(network, "name", attr_type=basestring)
154 155 156

    network = util.get_network(network_id, request.user_uniq, for_update=True)
    if network.public:
157
        raise api.faults.Forbidden("Cannot rename the public network.")
158 159
    network = networks.rename(network, new_name)
    return render_network(request, network_to_dict(network), 200)
160

161

162
@api.api_method(http_method='DELETE', user_required=True, logger=log)
Marios Kogias's avatar
Marios Kogias committed
163
@transaction.commit_on_success
164
def delete_network(request, network_id):
Christos Stavrakakis's avatar
Christos Stavrakakis committed
165
    log.info('delete_network %s', network_id)
166 167
    network = util.get_network(network_id, request.user_uniq, for_update=True)
    if network.public:
168
        raise api.faults.Forbidden("Cannot delete the public network.")
169
    networks.delete(network)
170
    return HttpResponse(status=204)
171 172


Marios Kogias's avatar
Marios Kogias committed
173 174 175 176
def network_to_dict(network, detail=True):
    d = {'id': str(network.id), 'name': network.name}
    d['links'] = util.network_to_links(network.id)
    if detail:
177 178 179 180
        # Loop over subnets. Do not perform any extra query because of prefetch
        # related!
        subnet_ids = []
        for subnet in network.subnets.all():
Christos Stavrakakis's avatar
Christos Stavrakakis committed
181
            subnet_ids.append(subnet.id)
182

183
        state = "SNF:DRAINED" if network.drained else network.state
Marios Kogias's avatar
Marios Kogias committed
184 185 186
        d['user_id'] = network.userid
        d['tenant_id'] = network.userid
        d['type'] = network.flavor
187 188
        d['updated'] = api.utils.isoformat(network.updated)
        d['created'] = api.utils.isoformat(network.created)
189
        d['status'] = state
Marios Kogias's avatar
Marios Kogias committed
190
        d['public'] = network.public
191
        d['router:external'] = network.external_router
Marios Kogias's avatar
Marios Kogias committed
192
        d['admin_state_up'] = True
193
        d['subnets'] = subnet_ids
194
        d['SNF:floating_ip_pool'] = network.floating_ip_pool
195
        d['deleted'] = network.deleted
Marios Kogias's avatar
Marios Kogias committed
196 197 198 199 200 201 202 203 204
    return d


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