Revision a96e84cf

b/snf-cyclades-app/synnefo/api/networks.py
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

  
1 34
from django.conf import settings
2 35
from django.conf.urls import patterns
3

  
4 36
from django.http import HttpResponse
5 37
from django.utils import simplejson as json
6 38
from django.db import transaction
7 39
from django.db.models import Q
8
from synnefo.db.pools import EmptyPool
9
from synnefo.db.utils import validate_mac
10
from django.conf import settings
11
from snf_django.lib import api
12
from snf_django.lib.api import utils
13
from synnefo.logic import backend
14 40
from django.template.loader import render_to_string
41

  
42
from snf_django.lib import api
43

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

  
18 48
from logging import getLogger
19 49

  
20
from synnefo import quotas
21

  
22 50
log = getLogger(__name__)
23 51

  
24 52
urlpatterns = patterns(
25 53
    'synnefo.api.networks',
26 54
    (r'^(?:/|.json|.xml)?$', 'demux'),
55
    (r'^/detail(?:.json|.xml)?$', 'list_networks', {'detail': True}),
27 56
    (r'^/(\w+)(?:/|.json|.xml)?$', 'network_demux'))
28 57

  
29 58

  
30 59
def demux(request):
31 60
    if request.method == 'GET':
32
        #return HttpResponse("in network get")
33 61
        return list_networks(request)
34 62
    elif request.method == 'POST':
35 63
        return create_network(request)
36
        #return HttpResponse("in network post")
37 64
    else:
38 65
        return api.api_method_not_allowed(request)
39 66

  
40 67

  
41
def network_demux(request, offset):
68
def network_demux(request, network_id):
42 69

  
43 70
    if request.method == 'GET':
44
        return get_network(request, offset)
45
        #return HttpResponse("in network det get")
71
        return get_network_details(request, network_id)
46 72
    elif request.method == 'DELETE':
47
        return delete_network(request, offset)
48
        #return HttpResponse("in network det delete")
73
        return delete_network(request, network_id)
49 74
    elif request.method == 'PUT':
50
        return update_network(request, offset)
51
        #return HttpResponse("in network det put")
75
        return update_network(request, network_id)
52 76
    else:
53 77
        return api.api_method_not_allowed(request)
54 78

  
......
57 81
def list_networks(request, detail=False):
58 82
    log.debug('list_networks detail=%s', detail)
59 83

  
60
    user_networks = Network.objects.filter(
61
        Q(userid=request.user_uniq) | Q(public=True))
84
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
85
                                           Q(public=True))\
86
                                   .prefetch_related("subnets")
62 87

  
63
    user_networks = utils.filter_modified_since(request, objects=user_networks)
88
    user_networks = api.utils.filter_modified_since(request,
89
                                                    objects=user_networks)
64 90

  
65
    networks = [network_to_dict(network, detail)
66
                for network in user_networks.order_by('id')]
91
    network_dicts = [network_to_dict(network, detail)
92
                     for network in user_networks.order_by('id')]
67 93

  
68 94
    if request.serialization == 'xml':
69 95
        data = render_to_string('list_networks.xml', {
70
            "networks": networks})
96
            "networks": network_dicts})
71 97
    else:
72
        data = json.dumps({'networks': networks})
98
        data = json.dumps({'networks': network_dicts})
73 99

  
74 100
    return HttpResponse(data, status=200)
75 101

  
76 102

  
77 103
@api.api_method(http_method='POST', user_required=True, logger=log)
78
@transaction.commit_manually
79 104
def create_network(request):
80
    try:
81
        user_id = request.user_uniq
82
        req = utils.get_request_dict(request)
83
        log.info('create_network %s', req)
84
        try:
85
            d = req['network']
86
        except KeyError:
87
            raise api.faults.BadRequest("Malformed request")
88

  
89
        try:
90
            flavor = d['type']
91
        except KeyError:
92
            raise api.faults.BadRequest("Missing request parameter 'type'")
93

  
94
        if flavor not in Network.FLAVORS.keys():
95
            raise api.faults.BadRequest("Invalid network type '%s'"
96
                                        % flavor)
97
        if flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
98
            msg = "Can not create network of type '%s'"
99
            raise api.faults.Forbidden(msg % flavor)
100

  
101
        try:
102
            name = d['name']
103
        except KeyError:
104
            name = ""
105

  
106
        try:
107
            #mode, link, mac_prefix, tags = util.values_from_flavor(flavor)
108

  
109
            #validate_mac(mac_prefix + "0:00:00:00")
110
            network = Network.objects.create(
111
                name=name,
112
                userid=user_id,
113
                flavor=flavor,
114
                #mode=mode,
115
                #link=link,
116
                #mac_prefix=mac_prefix,
117
                #tags=tags,
118
                action='CREATE',
119
                state='ACTIVE')
120
        except EmptyPool:
121
            msg = "Failed to allocate resources for network of type: %s"
122
            log.error(msg, flavor)
123
            raise api.faults.ServiceUnavailable("Failed to allocate network\
124
                  resources")
125

  
126
        # Issue commission to Quotaholder and accept it since at the end of
127
        # this transaction the Network object will be created in the DB.
128
        # Note: the following call does a commit!
129
        #quotas.issue_and_accept_commission(network)
130
        # COME BACK....
131

  
132
    except:
133
        transaction.rollback()
134
        log.info("roll")
135
        raise
136
    else:
137
        transaction.commit()
138
        log.info("commit")
139
    networkdict = network_to_dict(network)
105
    userid = request.user_uniq
106
    req = api.utils.get_request_dict(request)
107
    log.info('create_network %s', req)
108

  
109
    network_dict = api.utils.get_attribute(req, "network")
110
    flavor = api.utils.get_attribute(network_dict, "type")
111

  
112
    if flavor not in Network.FLAVORS.keys():
113
        raise api.faults.BadRequest("Invalid network type '%s'" % flavor)
114
    if flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
115
        raise api.faults.Forbidden("Can not create network of type '%s'." %
116
                                   flavor)
117

  
118
    name = api.utils.get_attribute(network_dict, "name", required=False)
119
    if name is None:
120
        name = ""
121

  
122
    network = networks.create(userid=userid, name=name, flavor=flavor,
123
                              public=False)
124
    networkdict = network_to_dict(network, detail=True)
140 125
    response = render_network(request, networkdict, status=201)
141 126

  
142 127
    return response
143 128

  
144 129

  
145 130
@api.api_method(http_method='GET', user_required=True, logger=log)
146
def get_network(request, network_id):
131
def get_network_details(request, network_id):
147 132
    log.debug('get_network_details %s', network_id)
148
    net = util.get_network(network_id, request.user_uniq)
133
    network = util.get_network(network_id, request.user_uniq)
134
    return render_network(request, network_to_dict(network, detail=True))
149 135

  
150
    #needs discussion
151
    if net.deleted:
152
        raise api.faults.BadRequest("Network has been deleted.")
153
    else:
154
        netdict = network_to_dict(net)
155
        return render_network(request, netdict)
136

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

  
141
    network = api.utils.get_attribute(info, "network", required=True)
142
    new_name = api.utils.get_attribute(network, "name")
143

  
144
    network = util.get_network(network_id, request.user_uniq, for_update=True)
145
    if network.public:
146
        raise api.faults.Forbidden("Can not rename the public network.")
147
    network = networks.rename(network, new_name)
148
    return render_network(request, network_to_dict(network), 200)
156 149

  
157 150

  
158 151
@api.api_method(http_method='DELETE', user_required=True, logger=log)
159 152
@transaction.commit_on_success
160 153
def delete_network(request, network_id):
161 154
    log.info('delete_network %s', network_id)
162
    net = util.get_network(network_id, request.user_uniq, for_update=True)
163

  
164
    log.info(net.name)
165
    if net.public:
166
        raise api.faults.Forbidden('Can not delete the public network.')
167

  
168
    if net.deleted:
169
        raise api.faults.BadRequest("Network has been deleted.")
170

  
171
    if net.machines.all():  # Nics attached on network
172
        #raise api.faults.NetworkInUse('Machines are connected to network.')
173
        #edit to return with 409
174
        return HttpResponse("Network in use", status=409)
175

  
176
    #check if there are any floating ips reserved
177
    #if net.floating_ips.all():
178
    #    #raise api.faults.NetworkInUse('Machines are connected to network.')
179
    #    #edit to return with 409
180
    #    return HttpResponse("Network in use", status=409)
181

  
182
    net.action = 'DESTROY'
183
    '''
184
    skip the backend part...
185
    backend_networks = net.backend_networks.exclude(operstate="DELETED")
186
    for bnet in backend_networks:
187
        backend.delete_network(net, bnet.backend)
188
    if not backend_networks:
189
        backend.update_network_state(net)
190
    '''
191

  
192
    #delete all the subnets
193

  
194
    for s in net.subnets.all():
195
        s.deleted = True
196

  
197
    #the following has to leave when fix the backend thing
198
    net.deleted = True
199
    net.save()
200

  
155
    network = util.get_network(network_id, request.user_uniq, for_update=True)
156
    if network.public:
157
        raise api.faults.Forbidden("Can not delete the public network.")
158
    networks.delete(network)
201 159
    return HttpResponse(status=204)
202 160

  
203 161

  
204
@api.api_method(http_method='PUT', user_required=True, logger=log)
205
def update_network(request, network_id):
206
    '''
207
    You can update only name
208
    '''
209
    net = util.get_network(network_id, request.user_uniq, for_update=True)
210
    info = utils.get_request_dict(request)
211

  
212
    updatable = set(["name"])
213
    try:
214
        new_vals = info["network"]
215
    except KeyError:
216
        raise api.faults.BadRequest("Malformed request")
217

  
218
    for key, val in new_vals.iteritems():
219
        if key in updatable:
220
            setattr(net, key, val)
221
        else:
222
            raise api.faults.BadRequest("Wrong field update")
223
    net.save()
224
    netdict = network_to_dict(net)
225
    return render_network(request, netdict, 200)
226

  
227

  
228 162
def network_to_dict(network, detail=True):
229 163
    d = {'id': str(network.id), 'name': network.name}
230 164
    d['links'] = util.network_to_links(network.id)
......
232 166
        d['user_id'] = network.userid
233 167
        d['tenant_id'] = network.userid
234 168
        d['type'] = network.flavor
235
        d['updated'] = utils.isoformat(network.updated)
236
        d['created'] = utils.isoformat(network.created)
169
        d['updated'] = api.utils.isoformat(network.updated)
170
        d['created'] = api.utils.isoformat(network.created)
237 171
        d['status'] = network.state
238 172
        d['public'] = network.public
239 173
        d['external_router'] = network.external_router
240
        d['external_router'] = network.external_router
241 174
        d['admin_state_up'] = True
242
        subnet_cidr = [s.id for s in network.subnets.all()]
243
        d['subnets'] = subnet_cidr
175
        d['subnets'] = list(network.subnets.values_list('id', flat=True))
244 176
    return d
245 177

  
246 178

  
b/snf-cyclades-app/synnefo/api/util.py
212 212

  
213 213
    try:
214 214
        network_id = int(network_id)
215
        objects = Network.objects
215
        objects = Network.objects.prefetch_related("subnets")
216 216
        if for_update:
217 217
            objects = objects.select_for_update()
218 218
        network = objects.get(Q(userid=user_id) | Q(public=True),
b/snf-cyclades-app/synnefo/logic/backend.py
466 466
            return
467 467
        elif not network.public:
468 468
            log.warning("Network %s does not have an owner!", network.id)
469

  
470
        # TODO!!!!!
471
        # Set all subnets as deleted
472
        network.subnets.update(deleted=True)
473
        # And delete the IP pools
474
        network.subnets.ip_pools.all().delete()
469 475
    network.save()
470 476

  
471 477

  
b/snf-cyclades-app/synnefo/logic/networks.py
65 65

  
66 66

  
67 67
@transaction.commit_on_success
68
def create(user_id, name, flavor, subnet=None, gateway=None, subnet6=None,
68
def create(userid, name, flavor, subnet=None, gateway=None, subnet6=None,
69 69
           gateway6=None, public=False, dhcp=True, link=None, mac_prefix=None,
70 70
           mode=None, floating_ip_pool=False, tags=None, backends=None,
71 71
           lazy_create=True):
......
80 80
        raise faults.BadRequest("Can not override PHYSICAL_VLAN link")
81 81

  
82 82
    if subnet is None and floating_ip_pool:
83
        raise faults.BadRequest("IPv6 only networks can not be"
83
        raise faults.BadRequest("IPv6 only networks can not be floating"
84 84
                                " pools.")
85 85
    # Check that network parameters are valid
86 86
    validate_network_params(subnet, gateway, subnet6, gateway6)
......
107 107

  
108 108
    network = Network.objects.create(
109 109
        name=name,
110
        userid=user_id,
110
        userid=userid,
111 111
        flavor=flavor,
112 112
        mode=mode,
113 113
        link=link,
......
160 160
@network_command("DESTROY")
161 161
def delete(network):
162 162
    if network.machines.exists():
163
        raise faults.NetworkInUse("Can not delete network. Servers connected"
164
                                  " to this network exists.")
165
    if network.floating_ips.filter(deleted=False).exists():
163
        raise faults.Conflict("Can not delete network. Servers connected"
164
                              " to this network exists.")
165
    if network.ips.filter(deleted=False, floating_ip=True).exists():
166 166
        msg = "Can not delete netowrk. Network has allocated floating IPs."
167
        raise faults.NetworkInUse(msg)
167
        raise faults.Conflict(msg)
168 168

  
169 169
    network.action = "DESTROY"
170 170
    network.save()
......
180 180

  
181 181
def validate_network_params(subnet=None, gateway=None, subnet6=None,
182 182
                            gateway6=None):
183
    if (subnet is None) and (subnet6 is None):
184
        raise faults.BadRequest("subnet or subnet6 is required")
185

  
186 183
    if subnet:
187 184
        try:
188 185
            # Use strict option to not all subnets with host bits set
b/snf-cyclades-app/synnefo/logic/tests/networks.py
44 44
    def test_create(self):
45 45
        kwargs = {
46 46
            "name": "test",
47
            "user_id": "user",
47
            "userid": "user",
48 48
            "subnet": "192.168.20.0/24",
49 49
            "flavor": "CUSTOM",
50 50
        }
......
130 130
    def test_create_network_ipv6(self):
131 131
        kwargs = {
132 132
            "name": "test",
133
            "user_id": "user",
133
            "userid": "user",
134 134
            "flavor": "CUSTOM",
135 135
            "subnet6": "2001:648:2ffc:1112::/64",
136 136
        }
b/snf-django-lib/snf_django/lib/api/utils.py
133 133
        return modified_objs
134 134
    else:
135 135
        return objects.filter(deleted=False)
136

  
137

  
138
def get_attribute(request, attribute, required=True):
139
    value = request.get(attribute, None)
140
    if required and value is None:
141
        raise faults.BadRequest("Malformed request. Missing attribute '%s'." %
142
                                attribute)
143
    return value

Also available in: Unified diff