Revision 0baa1e3d

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
from django.conf import settings
34 2
from django.conf.urls import patterns
35 3

  
36
from django.db.models import Q
37 4
from django.http import HttpResponse
38
from django.template.loader import render_to_string
39 5
from django.utils import simplejson as json
40

  
6
from django.db import transaction
7
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
41 11
from snf_django.lib import api
42
from snf_django.lib.api import faults, utils
12
from snf_django.lib.api import utils
13
from synnefo.logic import backend
14
from django.template.loader import render_to_string
43 15
from synnefo.api import util
44
from synnefo.api.servers import network_actions
45 16
from synnefo.db.models import Network
46
from synnefo.logic import networks
47

  
48 17

  
49 18
from logging import getLogger
19

  
20
from synnefo import quotas
21

  
50 22
log = getLogger(__name__)
51 23

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

  
26
     (r'^(?:/|.json|.xml)?$', 'demux'),
27
     (r'^/(\w+)(?:/|.json|.xml)?$', 'network_demux'))
60 28

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

  
69 39

  
70
def network_demux(request, network_id):
40
def network_demux(request, offset):
41

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

  
80 54

  
81
def network_to_dict(network, user_id, detail=True):
82
    d = {'id': str(network.id), 'name': network.name}
83
    d['links'] = util.network_to_links(network.id)
84
    if detail:
85
        d['user_id'] = network.userid
86
        d['tenant_id'] = network.userid
87
        d['cidr'] = network.subnet
88
        d['cidr6'] = network.subnet6
89
        d['gateway'] = network.gateway
90
        d['gateway6'] = network.gateway6
91
        d['dhcp'] = network.dhcp
92
        d['type'] = network.flavor
93
        d['updated'] = utils.isoformat(network.updated)
94
        d['created'] = utils.isoformat(network.created)
95
        d['status'] = network.state
96
        d['public'] = network.public
97

  
98
        attachments = [nic.id
99
                       for nic in network.nics.filter(machine__userid=user_id)
100
                                              .filter(state="ACTIVE")
101
                                              .order_by('machine')]
102
        d['attachments'] = attachments
103
    return d
104

  
105

  
106
def render_network(request, networkdict, status=200):
107
    if request.serialization == 'xml':
108
        data = render_to_string('network.xml', {'network': networkdict})
109
    else:
110
        data = json.dumps({'network': networkdict})
111
    return HttpResponse(data, status=status)
112

  
113

  
114 55
@api.api_method(http_method='GET', user_required=True, logger=log)
115 56
def list_networks(request, detail=False):
116
    # Normal Response Codes: 200, 203
117
    # Error Response Codes: computeFault (400, 500),
118
    #                       serviceUnavailable (503),
119
    #                       unauthorized (401),
120
    #                       badRequest (400),
121
    #                       overLimit (413)
122

  
123 57
    log.debug('list_networks detail=%s', detail)
124
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
125
                                           Q(public=True))
58

  
59
    user_networks = Network.objects.filter(
60
        Q(userid=request.user_uniq) | Q(public=True))
61

  
126 62
    user_networks = utils.filter_modified_since(request, objects=user_networks)
127 63

  
128
    networks_dict = [network_to_dict(network, request.user_uniq, detail)
129
                     for network in user_networks.order_by('id')]
64
    networks = [network_to_dict(network, detail)
65
                for network in user_networks.order_by('id')]
130 66

  
131 67
    if request.serialization == 'xml':
132 68
        data = render_to_string('list_networks.xml', {
133
            'networks': networks_dict,
134
            'detail': detail})
69
            "networks": networks})
135 70
    else:
136
        data = json.dumps({'networks': networks_dict})
71
        data = json.dumps({'networks': networks})
137 72

  
138 73
    return HttpResponse(data, status=200)
139 74

  
140 75

  
141 76
@api.api_method(http_method='POST', user_required=True, logger=log)
77
@transaction.commit_manually
142 78
def create_network(request):
143
    # Normal Response Code: 202
144
    # Error Response Codes: computeFault (400, 500),
145
    #                       serviceUnavailable (503),
146
    #                       unauthorized (401),
147
    #                       badMediaType(415),
148
    #                       badRequest (400),
149
    #                       forbidden (403)
150
    #                       overLimit (413)
151

  
152
    req = utils.get_request_dict(request)
153
    log.info('create_network %s', req)
154
    user_id = request.user_uniq
155 79
    try:
156
        d = req['network']
157
        name = d['name']
158
    except KeyError:
159
        raise faults.BadRequest("Malformed request")
160

  
161
    # Get and validate flavor. Flavors are still exposed as 'type' in the
162
    # API.
163
    flavor = d.get("type", None)
164
    if flavor is None:
165
        raise faults.BadRequest("Missing request parameter 'type'")
166
    elif flavor not in Network.FLAVORS.keys():
167
        raise faults.BadRequest("Invalid network type '%s'" % flavor)
168
    elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
169
        raise faults.Forbidden("Can not create network of type '%s'" %
170
                               flavor)
171

  
172
    public = d.get("public", False)
173
    if public:
174
        raise faults.Forbidden("Can not create a public network.")
175

  
176
    dhcp = d.get('dhcp', True)
177

  
178
    # Get and validate network parameters
179
    subnet = d.get('cidr', '192.168.1.0/24')
180
    subnet6 = d.get('cidr6', None)
181
    gateway = d.get('gateway', None)
182
    gateway6 = d.get('gateway6', None)
183

  
184
    network = networks.create(user_id=user_id, name=name, flavor=flavor,
185
                              subnet=subnet, gateway=gateway, subnet6=subnet6,
186
                              gateway6=gateway6, dhcp=dhcp, public=False)
187

  
188
    networkdict = network_to_dict(network, request.user_uniq)
189
    response = render_network(request, networkdict, status=202)
80
        user_id = request.user_uniq
81
        req = utils.get_request_dict(request)
82
        log.info('create_network %s', req)
83
        try:
84
            d = req['network']
85
        except KeyError:
86
            raise api.faults.BadRequest("Malformed request")
87

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

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

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

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

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

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

  
131
    except:
132
        transaction.rollback()
133
        log.info("roll")
134
        raise
135
    else:
136
        transaction.commit()
137
        log.info("commit")
138
    networkdict = network_to_dict(network)
139
    response = render_network(request, networkdict, status=201)
190 140

  
191 141
    return response
192 142

  
193 143

  
194 144
@api.api_method(http_method='GET', user_required=True, logger=log)
195
def get_network_details(request, network_id):
196
    # Normal Response Codes: 200, 203
197
    # Error Response Codes: computeFault (400, 500),
198
    #                       serviceUnavailable (503),
199
    #                       unauthorized (401),
200
    #                       badRequest (400),
201
    #                       itemNotFound (404),
202
    #                       overLimit (413)
203

  
145
def get_network(request, network_id):
204 146
    log.debug('get_network_details %s', network_id)
205 147
    net = util.get_network(network_id, request.user_uniq)
206
    netdict = network_to_dict(net, request.user_uniq)
207
    return render_network(request, netdict)
208

  
209 148

  
210
@api.api_method(http_method='PUT', user_required=True, logger=log)
211
def update_network_name(request, network_id):
212
    # Normal Response Code: 204
213
    # Error Response Codes: computeFault (400, 500),
214
    #                       serviceUnavailable (503),
215
    #                       unauthorized (401),
216
    #                       badRequest (400),
217
    #                       forbidden (403)
218
    #                       badMediaType(415),
219
    #                       itemNotFound (404),
220
    #                       overLimit (413)
221

  
222
    req = utils.get_request_dict(request)
223
    log.info('update_network_name %s', network_id)
224

  
225
    try:
226
        name = req['network']['name']
227
    except (TypeError, KeyError):
228
        raise faults.BadRequest('Malformed request.')
229

  
230
    network = util.get_network(network_id, request.user_uniq)
231
    if network.public:
232
        raise faults.Forbidden('Can not rename the public network.')
233
    network = networks.rename(network, name)
234
    return HttpResponse(status=204)
149
    #needs discussion
150
    if net.deleted:
151
        raise api.faults.BadRequest("Network has been deleted.")
152
    else:
153
        netdict = network_to_dict(net)
154
        return render_network(request, netdict)
235 155

  
236 156

  
237 157
@api.api_method(http_method='DELETE', user_required=True, logger=log)
158
@transaction.commit_on_success
238 159
def delete_network(request, network_id):
239
    # Normal Response Code: 204
240
    # Error Response Codes: computeFault (400, 500),
241
    #                       serviceUnavailable (503),
242
    #                       unauthorized (401),
243
    #                       forbidden (403)
244
    #                       itemNotFound (404),
245
    #                       overLimit (413)
246

  
247 160
    log.info('delete_network %s', network_id)
248
    network = util.get_network(network_id, request.user_uniq, for_update=True)
249
    if network.public:
250
        raise faults.Forbidden('Can not delete the public network.')
251
    networks.delete(network)
161
    net = util.get_network(network_id, request.user_uniq, for_update=True)
162

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

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

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

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

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

  
191
    #delete all the subnets
192

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

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

  
252 200
    return HttpResponse(status=204)
253 201

  
254 202

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

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

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

  
259
@api.api_method(http_method='POST', user_required=True, logger=log)
260
def demux_network_action(request, network_id):
261
    req = utils.get_request_dict(request)
262
    log.debug('network_action %s %s', network_id, req)
263
    if len(req) != 1:
264
        raise faults.BadRequest('Malformed request.')
265 226

  
266
    net = util.get_network(network_id, request.user_uniq)
267
    if net.public:
268
        raise faults.Forbidden('Can not modify the public network.')
269
    if net.deleted:
270
        raise faults.BadRequest("Network has been deleted.")
271

  
272
    action = req.keys()[0]
273
    if key_to_action(action) not in [x[0] for x in Network.ACTIONS]:
274
        raise faults.BadRequest("Action %s not supported." % action)
275
    action_args = req[action]
276
    if not isinstance(action_args, dict):
277
        raise faults.BadRequest("Invalid argument.")
278
    return network_actions[action](request, net, action_args)
227
def network_to_dict(network, detail=True):
228
    d = {'id': str(network.id), 'name': network.name}
229
    d['links'] = util.network_to_links(network.id)
230
    if detail:
231
        d['user_id'] = network.userid
232
        d['tenant_id'] = network.userid
233
        d['type'] = network.flavor
234
        d['updated'] = utils.isoformat(network.updated)
235
        d['created'] = utils.isoformat(network.created)
236
        d['status'] = network.state
237
        d['public'] = network.public
238
        d['external_router'] = network.external_router
239
        d['external_router'] = network.external_router
240
        d['admin_state_up'] = True
241
        subnet_cidr = [s.id for s in network.subnets.all()]
242
        d['subnets'] = subnet_cidr
243
    return d
244

  
245

  
246
def render_network(request, networkdict, status=200):
247
    if request.serialization == 'xml':
248
        data = render_to_string('network.xml', {'network': networkdict})
249
    else:
250
        data = json.dumps({'network': networkdict})
251
    return HttpResponse(data, status=status)
b/snf-cyclades-app/synnefo/api/old_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
from django.conf import settings
34
from django.conf.urls import patterns
35

  
36
from django.db.models import Q
37
from django.http import HttpResponse
38
from django.template.loader import render_to_string
39
from django.utils import simplejson as json
40

  
41
from snf_django.lib import api
42
from snf_django.lib.api import faults, utils
43
from synnefo.api import util
44
from synnefo.api.servers import network_actions
45
from synnefo.db.models import Network
46
from synnefo.logic import networks
47

  
48

  
49
from logging import getLogger
50
log = getLogger(__name__)
51

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

  
60

  
61
def demux(request):
62
    if request.method == 'GET':
63
        return list_networks(request)
64
    elif request.method == 'POST':
65
        return create_network(request)
66
    else:
67
        return api.api_method_not_allowed(request)
68

  
69

  
70
def network_demux(request, network_id):
71
    if request.method == 'GET':
72
        return get_network_details(request, network_id)
73
    elif request.method == 'PUT':
74
        return update_network_name(request, network_id)
75
    elif request.method == 'DELETE':
76
        return delete_network(request, network_id)
77
    else:
78
        return api.api_method_not_allowed(request)
79

  
80

  
81
def network_to_dict(network, user_id, detail=True):
82
    d = {'id': str(network.id), 'name': network.name}
83
    d['links'] = util.network_to_links(network.id)
84
    if detail:
85
        d['user_id'] = network.userid
86
        d['tenant_id'] = network.userid
87
        d['cidr'] = network.subnet
88
        d['cidr6'] = network.subnet6
89
        d['gateway'] = network.gateway
90
        d['gateway6'] = network.gateway6
91
        d['dhcp'] = network.dhcp
92
        d['type'] = network.flavor
93
        d['updated'] = utils.isoformat(network.updated)
94
        d['created'] = utils.isoformat(network.created)
95
        d['status'] = network.state
96
        d['public'] = network.public
97

  
98
        attachments = [nic.id
99
                       for nic in network.nics.filter(machine__userid=user_id)
100
                                              .filter(state="ACTIVE")
101
                                              .order_by('machine')]
102
        d['attachments'] = attachments
103
    return d
104

  
105

  
106
def render_network(request, networkdict, status=200):
107
    if request.serialization == 'xml':
108
        data = render_to_string('network.xml', {'network': networkdict})
109
    else:
110
        data = json.dumps({'network': networkdict})
111
    return HttpResponse(data, status=status)
112

  
113

  
114
@api.api_method(http_method='GET', user_required=True, logger=log)
115
def list_networks(request, detail=False):
116
    # Normal Response Codes: 200, 203
117
    # Error Response Codes: computeFault (400, 500),
118
    #                       serviceUnavailable (503),
119
    #                       unauthorized (401),
120
    #                       badRequest (400),
121
    #                       overLimit (413)
122

  
123
    log.debug('list_networks detail=%s', detail)
124
    user_networks = Network.objects.filter(Q(userid=request.user_uniq) |
125
                                           Q(public=True))
126
    user_networks = utils.filter_modified_since(request, objects=user_networks)
127

  
128
    networks_dict = [network_to_dict(network, request.user_uniq, detail)
129
                     for network in user_networks.order_by('id')]
130

  
131
    if request.serialization == 'xml':
132
        data = render_to_string('list_networks.xml', {
133
            'networks': networks_dict,
134
            'detail': detail})
135
    else:
136
        data = json.dumps({'networks': networks_dict})
137

  
138
    return HttpResponse(data, status=200)
139

  
140

  
141
@api.api_method(http_method='POST', user_required=True, logger=log)
142
def create_network(request):
143
    # Normal Response Code: 202
144
    # Error Response Codes: computeFault (400, 500),
145
    #                       serviceUnavailable (503),
146
    #                       unauthorized (401),
147
    #                       badMediaType(415),
148
    #                       badRequest (400),
149
    #                       forbidden (403)
150
    #                       overLimit (413)
151

  
152
    req = utils.get_request_dict(request)
153
    log.info('create_network %s', req)
154
    user_id = request.user_uniq
155
    try:
156
        d = req['network']
157
        name = d['name']
158
    except KeyError:
159
        raise faults.BadRequest("Malformed request")
160

  
161
    # Get and validate flavor. Flavors are still exposed as 'type' in the
162
    # API.
163
    flavor = d.get("type", None)
164
    if flavor is None:
165
        raise faults.BadRequest("Missing request parameter 'type'")
166
    elif flavor not in Network.FLAVORS.keys():
167
        raise faults.BadRequest("Invalid network type '%s'" % flavor)
168
    elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
169
        raise faults.Forbidden("Can not create network of type '%s'" %
170
                               flavor)
171

  
172
    public = d.get("public", False)
173
    if public:
174
        raise faults.Forbidden("Can not create a public network.")
175

  
176
    dhcp = d.get('dhcp', True)
177

  
178
    # Get and validate network parameters
179
    subnet = d.get('cidr', '192.168.1.0/24')
180
    subnet6 = d.get('cidr6', None)
181
    gateway = d.get('gateway', None)
182
    gateway6 = d.get('gateway6', None)
183

  
184
    network = networks.create(user_id=user_id, name=name, flavor=flavor,
185
                              subnet=subnet, gateway=gateway, subnet6=subnet6,
186
                              gateway6=gateway6, dhcp=dhcp, public=False)
187

  
188
    networkdict = network_to_dict(network, request.user_uniq)
189
    response = render_network(request, networkdict, status=202)
190

  
191
    return response
192

  
193

  
194
@api.api_method(http_method='GET', user_required=True, logger=log)
195
def get_network_details(request, network_id):
196
    # Normal Response Codes: 200, 203
197
    # Error Response Codes: computeFault (400, 500),
198
    #                       serviceUnavailable (503),
199
    #                       unauthorized (401),
200
    #                       badRequest (400),
201
    #                       itemNotFound (404),
202
    #                       overLimit (413)
203

  
204
    log.debug('get_network_details %s', network_id)
205
    net = util.get_network(network_id, request.user_uniq)
206
    netdict = network_to_dict(net, request.user_uniq)
207
    return render_network(request, netdict)
208

  
209

  
210
@api.api_method(http_method='PUT', user_required=True, logger=log)
211
def update_network_name(request, network_id):
212
    # Normal Response Code: 204
213
    # Error Response Codes: computeFault (400, 500),
214
    #                       serviceUnavailable (503),
215
    #                       unauthorized (401),
216
    #                       badRequest (400),
217
    #                       forbidden (403)
218
    #                       badMediaType(415),
219
    #                       itemNotFound (404),
220
    #                       overLimit (413)
221

  
222
    req = utils.get_request_dict(request)
223
    log.info('update_network_name %s', network_id)
224

  
225
    try:
226
        name = req['network']['name']
227
    except (TypeError, KeyError):
228
        raise faults.BadRequest('Malformed request.')
229

  
230
    network = util.get_network(network_id, request.user_uniq)
231
    if network.public:
232
        raise faults.Forbidden('Can not rename the public network.')
233
    network = networks.rename(network, name)
234
    return HttpResponse(status=204)
235

  
236

  
237
@api.api_method(http_method='DELETE', user_required=True, logger=log)
238
def delete_network(request, network_id):
239
    # Normal Response Code: 204
240
    # Error Response Codes: computeFault (400, 500),
241
    #                       serviceUnavailable (503),
242
    #                       unauthorized (401),
243
    #                       forbidden (403)
244
    #                       itemNotFound (404),
245
    #                       overLimit (413)
246

  
247
    log.info('delete_network %s', network_id)
248
    network = util.get_network(network_id, request.user_uniq, for_update=True)
249
    if network.public:
250
        raise faults.Forbidden('Can not delete the public network.')
251
    networks.delete(network)
252
    return HttpResponse(status=204)
253

  
254

  
255
def key_to_action(action):
256
    return action.upper()
257

  
258

  
259
@api.api_method(http_method='POST', user_required=True, logger=log)
260
def demux_network_action(request, network_id):
261
    req = utils.get_request_dict(request)
262
    log.debug('network_action %s %s', network_id, req)
263
    if len(req) != 1:
264
        raise faults.BadRequest('Malformed request.')
265

  
266
    net = util.get_network(network_id, request.user_uniq)
267
    if net.public:
268
        raise faults.Forbidden('Can not modify the public network.')
269
    if net.deleted:
270
        raise faults.BadRequest("Network has been deleted.")
271

  
272
    action = req.keys()[0]
273
    if key_to_action(action) not in [x[0] for x in Network.ACTIONS]:
274
        raise faults.BadRequest("Action %s not supported." % action)
275
    action_args = req[action]
276
    if not isinstance(action_args, dict):
277
        raise faults.BadRequest("Invalid argument.")
278
    return network_actions[action](request, net, action_args)
b/snf-cyclades-app/synnefo/api/tests/__init__.py
38 38
from .images import *
39 39
from .versions import *
40 40
from .extensions import *
41
from .floating_ips import *
41
#from .floating_ips import *
b/snf-cyclades-app/synnefo/api/tests/networks.py
1
# Copyright 2012 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
import json
35
from mock import patch
36

  
37
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
38
from synnefo.db.models import Network
39
from synnefo.db import models_factory as mfactory
1
from snf_django.utils.testing import BaseAPITest
2
from django.utils import simplejson as json
40 3
from synnefo.cyclades_settings import cyclades_services
41 4
from synnefo.lib.services import get_service_path
42 5
from synnefo.lib import join_urls
6
import json
7
import synnefo.db.models_factory as dbmf
43 8

  
44 9

  
45
class ComputeAPITest(BaseAPITest):
46
    def __init__(self, *args, **kwargs):
47
        super(ComputeAPITest, self).__init__(*args, **kwargs)
48
        self.compute_path = get_service_path(cyclades_services, 'compute',
10
COMPUTE_URL = get_service_path(cyclades_services, 'compute',
49 11
                                             version='v2.0')
12
NETWORKS_URL = join_urls(COMPUTE_URL, "networks/")
50 13

  
51
    def myget(self, path, *args, **kwargs):
52
        path = join_urls(self.compute_path, path)
53
        return self.get(path, *args, **kwargs)
54

  
55
    def myput(self, path, *args, **kwargs):
56
        path = join_urls(self.compute_path, path)
57
        return self.put(path, *args, **kwargs)
58 14

  
59
    def mypost(self, path, *args, **kwargs):
60
        path = join_urls(self.compute_path, path)
61
        return self.post(path, *args, **kwargs)
15
class NetworkTest(BaseAPITest):
62 16

  
63
    def mydelete(self, path, *args, **kwargs):
64
        path = join_urls(self.compute_path, path)
65
        return self.delete(path, *args, **kwargs)
66

  
67

  
68
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
69
class NetworkAPITest(ComputeAPITest):
70
    def setUp(self):
71
        self.mac_prefixes = mfactory.MacPrefixPoolTableFactory()
72
        self.bridges = mfactory.BridgePoolTableFactory(base="link")
73
        self.user = 'dummy-user'
74
        self.net1 = mfactory.NetworkFactory(userid=self.user)
75
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user)
76
        self.nic1 = mfactory.NetworkInterfaceFactory(network=self.net1,
77
                                                     machine=self.vm1)
78
        self.nic2 = mfactory.NetworkInterfaceFactory(network=self.net1,
79
                                                     machine=self.vm1)
80
        self.net2 = mfactory.NetworkFactory(userid=self.user)
81
        self.nic3 = mfactory.NetworkInterfaceFactory(network=self.net2)
82
        super(NetworkAPITest, self).setUp()
83

  
84
    def assertNetworksEqual(self, db_net, api_net, detail=False):
85
        self.assertEqual(str(db_net.id), api_net["id"])
86
        self.assertEqual(db_net.name, api_net['name'])
87
        if detail:
88
            self.assertEqual(db_net.state, api_net['status'])
89
            self.assertEqual(db_net.flavor, api_net['type'])
90
            self.assertEqual(db_net.subnet, api_net['cidr'])
91
            self.assertEqual(db_net.subnet6, api_net['cidr6'])
92
            self.assertEqual(db_net.gateway, api_net['gateway'])
93
            self.assertEqual(db_net.gateway6, api_net['gateway6'])
94
            self.assertEqual(db_net.dhcp, api_net['dhcp'])
95
            self.assertEqual(db_net.public, api_net['public'])
96
            db_nics = [nic.id for nic in
97
                       db_net.nics.filter(machine__userid=db_net.userid)]
98
            self.assertEqual(db_nics, api_net['attachments'])
17
    def test_list_networks(self):
18
        response = self.get(NETWORKS_URL)
19
        self.assertSuccess(response)
20
        networks = json.loads(response.content)
21
        self.assertEqual(networks, {"networks": []})
99 22

  
100
    def test_create_network_1(self, mrapi):
23
    def test_create_network(self):
101 24
        request = {
102
            'network': {'name': 'foo', "type": "MAC_FILTERED"}
25
            "network": {
26
                "name": "sample_network",
27
                "type": "MAC_FILTERED"
28
                }
103 29
        }
104
        with mocked_quotaholder():
105
            response = self.mypost('networks/', 'user1',
106
                                   json.dumps(request), 'json')
107
        self.assertEqual(response.status_code, 202)
108
        db_networks = Network.objects.filter(userid='user1')
109
        self.assertEqual(len(db_networks), 1)
110
        db_net = db_networks[0]
111
        api_net = json.loads(response.content)['network']
112
        self.assertNetworksEqual(db_net, api_net)
113
        mrapi.CreateNetwork.assert_called()
114
        mrapi.ConnectNetwork.assert_called()
30
        response = self.post(NETWORKS_URL, params=json.dumps(request))
31
        code = response.status_code
32
        self.assertEqual(code, 201)
115 33

  
116
    def test_invalid_data_1(self, mrapi):
117
        """Test invalid flavor"""
118
        request = {
119
            'network': {'name': 'foo', 'type': 'LoLo'}
120
            }
121
        response = self.mypost('networks/', 'user1',
122
                               json.dumps(request), 'json')
123
        self.assertBadRequest(response)
124
        self.assertEqual(len(Network.objects.filter(userid='user1')), 0)
125

  
126
    def test_invalid_data_2(self, mrapi):
127
        """Test invalid data/subnet"""
128
        request = {
129
            'network': {'name': 'foo',
130
                        'cidr': '10.0.0.0/8', "type":
131
                        "MAC_FILTERED"}
132
        }
133
        response = self.mypost('networks/', 'user1',
134
                               json.dumps(request), 'json')
135
        self.assertFault(response, 413, "overLimit")
34
    def test_get_unfound_network(self):
35
        url = join_urls(NETWORKS_URL, "123")
36
        response = self.get(url)
37
        self.assertItemNotFound(response)
136 38

  
137
    def test_invalid_data_3(self, mrapi):
138
        """Test unauthorized to create public network"""
139
        request = {
140
            'network': {"name": 'foo',
141
                        "public": "True",
142
                        "type": "MAC_FILTERED"}
143
            }
144
        response = self.mypost('networks/', 'user1',
145
                               json.dumps(request), 'json')
146
        self.assertFault(response, 403, "forbidden")
39
    def test_get_network(self):
40
        test_net = dbmf.NetworkFactory.create()
41
        url = join_urls(NETWORKS_URL, str(test_net.id))
42
        response = self.get(url, user=test_net.userid)
43
        print response.content
44
        # validate response
45
        res = json.loads(response.content)
46
        net = res['network']
47
        keys = net.keys()
48
        must_send = set(["status", "subnets", "name", "admin_state_up",
49
                        "tenant_id", "id"])
50
        self.assertEqual(set(keys).issuperset(must_send), True)
51
        self.assertEqual(response.status_code, 200)
52

  
53
    def test_get_deleted_network(self):
54
        test_net = dbmf.NetworkFactory.create()
55
        url = join_urls(NETWORKS_URL, str(test_net.id))
56
        self.delete(url, user=test_net.userid)
57
        response = self.get(url, user=test_net.userid)
58
        self.assertEqual(response.status_code, 400)
147 59

  
148
    def test_invalid_data_4(self, mrapi):
149
        """Test unauthorized to create network not in settings"""
150
        request = {
151
            'network': {'name': 'foo', 'type': 'CUSTOM'}
152
            }
153
        response = self.mypost('networks/', 'user1',
154
                               json.dumps(request), 'json')
155
        self.assertFault(response, 403, "forbidden")
60
    def test_delete_unfound_network(self):
61
        url = join_urls(NETWORKS_URL, "123")
62
        response = self.delete(url)
63
        self.assertItemNotFound(response)
156 64

  
157
    def test_invalid_subnet(self, mrapi):
158
        """Test invalid subnet"""
159
        request = {
160
            'network': {'name': 'foo',
161
                        'cidr': '10.0.0.10/27',
162
                        "type": "MAC_FILTERED"}
163
        }
164
        response = self.mypost('networks/', 'user1',
165
                               json.dumps(request), 'json')
166
        self.assertBadRequest(response)
65
    def test_delete_network(self):
66
        test_net = dbmf.NetworkFactory.create()
67
        subnet = dbmf.IPv4SubnetFactory.create(network=test_net)
68
        url = join_urls(NETWORKS_URL, str(test_net.id))
69
        response = self.delete(url, user=test_net.userid)
70
        self.assertEqual(response.status_code, 204)
167 71

  
168
    def test_invalid_gateway_1(self, mrapi):
169
        request = {
170
            'network': {'name': 'foo',
171
                        'cidr': '10.0.0.0/28',
172
                        'gateway': '10.0.0.0.300'}
173
        }
174
        response = self.mypost('networks/', 'user1',
175
                               json.dumps(request), 'json')
176
        self.assertBadRequest(response)
72
    def test_delete_network_in_use(self):
73
        test_net = dbmf.NetworkFactory.create()
74
        test_iface = dbmf.NetworkInterfaceFactory.create(network=test_net)
75
        url = join_urls(NETWORKS_URL, str(test_net.id))
76
        response = self.delete(url, user=test_net.userid)
77
        self.assertEqual(response.status_code, 409)
177 78

  
178
    def test_invalid_gateway_2(self, mrapi):
179
        request = {
180
            'network': {'name': 'foo',
181
                        'cidr': '10.0.0.0/28',
182
                        'gateway': '10.2.0.1'}
183
        }
184
        response = self.mypost('networks/', 'user1',
185
                               json.dumps(request), 'json')
186
        self.assertBadRequest(response)
79
    def test_put_unfound_network(self):
80
        url = join_urls(NETWORKS_URL, "123")
81
        response = self.delete(url)
82
        self.assertItemNotFound(response)
187 83

  
188
    def test_invalid_network6(self, mrapi):
84
    def test_put_network(self):
85
        test_net = dbmf.NetworkFactory.create()
86
        url = join_urls(NETWORKS_URL, str(test_net.id))
189 87
        request = {
190
            'network': {'name': 'foo',
191
                        'cidr': '10.0.0.0/28',
192
                        'subnet6': '10.0.0.0/28',
193
                        'gateway': '10.2.0.1'}
88
            "network": {
89
                "name": "new_name"}
194 90
        }
195
        response = self.mypost('networks/', 'user1',
196
                               json.dumps(request), 'json')
197
        self.assertBadRequest(response)
91
        response = self.put(url, params=json.dumps(request),
92
                            user=test_net.userid)
93
        self.assertEqual(response.status_code, 200)
198 94

  
199
    def test_invalid_gateway6(self, mrapi):
95
    def test_put_network_wrong_data(self):
96
        test_net = dbmf.NetworkFactory.create()
97
        url = join_urls(NETWORKS_URL, str(test_net.id))
200 98
        request = {
201
            'network': {'name': 'foo',
202
                        'cidr': '10.0.0.0/28',
203
                        'subnet6': '2001:0db8:0123:4567:89ab:cdef:1234:5678',
204
                        'gateway': '10.2.0.1'}
99
            "network": {
100
                "wrong_field": "new_name"}
205 101
        }
206
        response = self.mypost('networks/', 'user1',
207
                               json.dumps(request), 'json')
208
        self.assertBadRequest(response)
209

  
210
    def test_list_networks(self, mrapi):
211
        """Test that expected list of networks is returned."""
212
        # Create a deleted network
213
        mfactory.NetworkFactory(userid=self.user, deleted=True)
214

  
215
        response = self.myget('networks/', self.user)
216
        self.assertSuccess(response)
217

  
218
        db_nets = Network.objects.filter(userid=self.user, deleted=False)
219
        api_nets = json.loads(response.content)["networks"]
220

  
221
        self.assertEqual(len(db_nets), len(api_nets))
222
        for api_net in api_nets:
223
            net_id = api_net['id']
224
            self.assertNetworksEqual(Network.objects.get(id=net_id), api_net)
225

  
226
    def test_list_networks_detail(self, mrapi):
227
        """Test that expected networks details are returned."""
228
        # Create a deleted network
229
        mfactory.NetworkFactory(userid=self.user, deleted=True)
230

  
231
        response = self.myget('networks/detail', self.user)
232
        self.assertSuccess(response)
233

  
234
        db_nets = Network.objects.filter(userid=self.user, deleted=False)
235
        api_nets = json.loads(response.content)["networks"]
236

  
237
        self.assertEqual(len(db_nets), len(api_nets))
238
        for api_net in api_nets:
239
            net_id = api_net['id']
240
            self.assertNetworksEqual(Network.objects.get(id=net_id), api_net,
241
                                     detail=True)
242

  
243
    def test_get_network_building_nics(self, mrapi):
244
        net = mfactory.NetworkFactory()
245
        machine = mfactory.VirtualMachineFactory(userid=net.userid)
246
        mfactory.NetworkInterfaceFactory(network=net, machine=machine,
247
                                         state="BUILDING")
248
        response = self.myget('networks/%d' % net.id, net.userid)
249
        self.assertSuccess(response)
250
        api_net = json.loads(response.content)["network"]
251
        self.assertEqual(len(api_net["attachments"]), 0)
252

  
253
    def test_network_details_1(self, mrapi):
254
        """Test that expected details for a network are returned"""
255
        response = self.myget('networks/%d' % self.net1.id, self.net1.userid)
256
        self.assertSuccess(response)
257
        api_net = json.loads(response.content)["network"]
258
        self.assertNetworksEqual(self.net1, api_net, detail=True)
259

  
260
    def test_invalid_network(self, mrapi):
261
        """Test details for non-existing network."""
262
        response = self.myget('networks/%d' % 42, self.net1.userid)
263
        self.assertItemNotFound(response)
264

  
265
    def test_rename_network(self, mrapi):
266
        request = {'network': {'name': "new_name"}}
267
        response = self.myput('networks/%d' % self.net2.id,
268
                              self.net2.userid, json.dumps(request), 'json')
269
        self.assertEqual(response.status_code, 204)
270
        self.assertEqual(Network.objects.get(id=self.net2.id).name, "new_name")
271
        # Check invalid
272
        request = {'name': "new_name"}
273
        response = self.myput('networks/%d' % self.net2.id,
274
                              self.net2.userid, json.dumps(request), 'json')
275
        self.assertBadRequest(response)
276

  
277
    def test_rename_deleted_network(self, mrapi):
278
        net = mfactory.NetworkFactory(deleted=True)
279
        request = {'network': {'name': "new_name"}}
280
        response = self.myput('networks/%d' % net.id,
281
                              net.userid, json.dumps(request), 'json')
282
        self.assertBadRequest(response)
283

  
284
    def test_rename_public_network(self, mrapi):
285
        net = mfactory.NetworkFactory(public=True)
286
        request = {'network': {'name': "new_name"}}
287
        response = self.myput('networks/%d' % net.id,
288
                              self.net2.userid, json.dumps(request), 'json')
289
        self.assertFault(response, 403, 'forbidden')
290

  
291
    def test_delete_network(self, mrapi):
292
        net = mfactory.NetworkFactory(deleted=False, state='ACTIVE',
293
                                      link="link-10")
294
        with mocked_quotaholder():
295
            response = self.mydelete('networks/%d' % net.id, net.userid)
296
        self.assertEqual(response.status_code, 204)
297
        net = Network.objects.get(id=net.id, userid=net.userid)
298
        self.assertEqual(net.action, 'DESTROY')
299
        mrapi.DeleteNetwork.assert_called()
300

  
301
    def test_delete_public_network(self, mrapi):
302
        net = mfactory.NetworkFactory(public=True)
303
        response = self.mydelete('networks/%d' % net.id, self.net2.userid)
304
        self.assertFault(response, 403, 'forbidden')
305
        self.assertFalse(mrapi.called)
306

  
307
    def test_delete_deleted_network(self, mrapi):
308
        net = mfactory.NetworkFactory(deleted=True)
309
        response = self.mydelete('networks/%d' % net.id, net.userid)
310
        self.assertBadRequest(response)
311

  
312
    def test_delete_network_in_use(self, mrapi):
313
        net = self.net1
314
        response = self.mydelete('networks/%d' % net.id, net.userid)
315
        self.assertFault(response, 421, 'networkInUse')
316
        self.assertFalse(mrapi.called)
317

  
318
    def test_add_nic(self, mrapi):
319
        user = 'userr'
320
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
321
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
322
        mrapi().ModifyInstance.return_value = 1
323
        request = {'add': {'serverRef': vm.id}}
324
        response = self.mypost('networks/%d/action' % net.id,
325
                               net.userid, json.dumps(request), 'json')
326
        self.assertEqual(response.status_code, 202)
327

  
328
    def test_add_nic_to_deleted_network(self, mrapi):
329
        user = 'userr'
330
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user,
331
                                            operstate="ACTIVE")
332
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user,
333
                                      deleted=True)
334
        request = {'add': {'serverRef': vm.id}}
335
        response = self.mypost('networks/%d/action' % net.id,
336
                               net.userid, json.dumps(request), 'json')
337
        self.assertBadRequest(response)
338
        self.assertFalse(mrapi.called)
339

  
340
    def test_add_nic_to_public_network(self, mrapi):
341
        user = 'userr'
342
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
343
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user, public=True)
344
        request = {'add': {'serverRef': vm.id}}
345
        response = self.mypost('networks/%d/action' % net.id,
346
                               net.userid, json.dumps(request), 'json')
347
        self.assertFault(response, 403, 'forbidden')
348
        self.assertFalse(mrapi.called)
349

  
350
    def test_add_nic_malformed_1(self, mrapi):
351
        user = 'userr'
352
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
353
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
354
        request = {'add': {'serveRef': vm.id}}
355
        response = self.mypost('networks/%d/action' % net.id,
356
                               net.userid, json.dumps(request), 'json')
357
        self.assertBadRequest(response)
358
        self.assertFalse(mrapi.called)
359

  
360
    def test_add_nic_malformed_2(self, mrapi):
361
        user = 'userr'
362
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
363
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
364
        request = {'add': {'serveRef': [vm.id, 22]}}
365
        response = self.mypost('networks/%d/action' % net.id,
366
                               net.userid, json.dumps(request), 'json')
367
        self.assertBadRequest(response)
368
        self.assertFalse(mrapi.called)
369

  
370
    def test_add_nic_not_active(self, mrapi):
371
        """Test connecting VM to non-active network"""
372
        user = 'dummy'
373
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
374
        net = mfactory.NetworkFactory(state='PENDING', subnet='10.0.0.0/31',
375
                                      userid=user)
376
        request = {'add': {'serverRef': vm.id}}
377
        response = self.mypost('networks/%d/action' % net.id,
378
                               net.userid, json.dumps(request), 'json')
379
        # Test that returns BuildInProgress
380
        self.assertEqual(response.status_code, 409)
381
        self.assertFalse(mrapi.called)
382

  
383
    def test_add_nic_full_network(self, mrapi):
384
        """Test connecting VM to a full network"""
385
        user = 'userr'
386
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user,
387
                                            operstate="STARTED")
388
        net = mfactory.NetworkFactory(state='ACTIVE', subnet='10.0.0.0/30',
389
                                      userid=user, dhcp=True)
390
        pool = net.get_pool()
391
        while not pool.empty():
392
            pool.get()
393
        pool.save()
394
        pool = net.get_pool()
395
        self.assertTrue(pool.empty())
396
        request = {'add': {'serverRef': vm.id}}
397
        response = self.mypost('networks/%d/action' % net.id,
398
                               net.userid, json.dumps(request), 'json')
399
        # Test that returns OverLimit
400
        self.assertEqual(response.status_code, 413)
401
        self.assertFalse(mrapi.called)
402

  
403
    def test_remove_nic(self, mrapi):
404
        user = 'userr'
405
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user,
406
                                            operstate="ACTIVE")
407
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
408
        nic = mfactory.NetworkInterfaceFactory(machine=vm, network=net)
409
        mrapi().ModifyInstance.return_value = 1
410
        request = {'remove': {'attachment': "%s" % nic.id}}
411
        response = self.mypost('networks/%d/action' % net.id,
412
                               net.userid, json.dumps(request), 'json')
413
        self.assertEqual(response.status_code, 202)
414
        vm.task = None
415
        vm.task_job_id = None
416
        vm.save()
417

  
418
    def test_remove_nic_malformed(self, mrapi):
419
        user = 'userr'
420
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
421
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
422
        nic = mfactory.NetworkInterfaceFactory(machine=vm, network=net)
423
        request = {'remove': {'att234achment': '%s' % nic.id}}
424
        response = self.mypost('networks/%d/action' % net.id,
425
                               net.userid, json.dumps(request), 'json')
426
        self.assertBadRequest(response)
427

  
428
    def test_remove_nic_malformed_2(self, mrapi):
429
        user = 'userr'
430
        vm = mfactory.VirtualMachineFactory(name='yo', userid=user)
431
        net = mfactory.NetworkFactory(state='ACTIVE', userid=user)
432
        request = {'remove': {'attachment': 'nic-%s' % vm.id}}
433
        response = self.mypost('networks/%d/action' % net.id,
434
                               net.userid, json.dumps(request), 'json')
435
        self.assertBadRequest(response)
436

  
437
    def test_catch_wrong_api_paths(self, *args):
438
        response = self.myget('nonexistent')
102
        response = self.put(url, params=json.dumps(request),
103
                            user=test_net.userid)
439 104
        self.assertEqual(response.status_code, 400)
440
        try:
441
            json.loads(response.content)
442
        except ValueError:
443
            self.assertTrue(False)
444 105

  
445
    def test_method_not_allowed(self, *args):
446
        # /networks/ allows only POST, GET
447
        response = self.myput('networks', '', '')
448
        self.assertMethodNotAllowed(response)
449
        response = self.mydelete('networks')
450
        self.assertMethodNotAllowed(response)
451

  
452
        # /networks/<srvid>/ allows only GET, PUT, DELETE
453
        response = self.mypost("networks/42")
454
        self.assertMethodNotAllowed(response)
106
    def test_put_no_data(self):
107
        test_net = dbmf.NetworkFactory.create()
108
        url = join_urls(NETWORKS_URL, str(test_net.id))
109
        response = self.put(url, params="", user=test_net.userid)
110
        self.assertEqual(response.status_code, 400)
b/snf-cyclades-app/synnefo/api/tests/old_networks.py
1
# Copyright 2012 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
import json
35
from mock import patch
36

  
37
from snf_django.utils.testing import BaseAPITest, mocked_quotaholder
38
from synnefo.db.models import Network
39
from synnefo.db import models_factory as mfactory
40
from synnefo.cyclades_settings import cyclades_services
41
from synnefo.lib.services import get_service_path
42
from synnefo.lib import join_urls
43

  
44

  
45
class ComputeAPITest(BaseAPITest):
46
    def __init__(self, *args, **kwargs):
47
        super(ComputeAPITest, self).__init__(*args, **kwargs)
48
        self.compute_path = get_service_path(cyclades_services, 'compute',
49
                                             version='v2.0')
50

  
51
    def myget(self, path, *args, **kwargs):
52
        path = join_urls(self.compute_path, path)
53
        return self.get(path, *args, **kwargs)
54

  
55
    def myput(self, path, *args, **kwargs):
56
        path = join_urls(self.compute_path, path)
57
        return self.put(path, *args, **kwargs)
58

  
59
    def mypost(self, path, *args, **kwargs):
60
        path = join_urls(self.compute_path, path)
61
        return self.post(path, *args, **kwargs)
62

  
63
    def mydelete(self, path, *args, **kwargs):
64
        path = join_urls(self.compute_path, path)
65
        return self.delete(path, *args, **kwargs)
66

  
67

  
68
@patch('synnefo.logic.rapi_pool.GanetiRapiClient')
69
class NetworkAPITest(ComputeAPITest):
70
    def setUp(self):
71
        self.mac_prefixes = mfactory.MacPrefixPoolTableFactory()
72
        self.bridges = mfactory.BridgePoolTableFactory(base="link")
73
        self.user = 'dummy-user'
74
        self.net1 = mfactory.NetworkFactory(userid=self.user)
75
        self.vm1 = mfactory.VirtualMachineFactory(userid=self.user)
76
        self.nic1 = mfactory.NetworkInterfaceFactory(network=self.net1,
77
                                                     machine=self.vm1)
78
        self.nic2 = mfactory.NetworkInterfaceFactory(network=self.net1,
79
                                                     machine=self.vm1)
80
        self.net2 = mfactory.NetworkFactory(userid=self.user)
81
        self.nic3 = mfactory.NetworkInterfaceFactory(network=self.net2)
82
        super(NetworkAPITest, self).setUp()
83

  
84
    def assertNetworksEqual(self, db_net, api_net, detail=False):
85
        self.assertEqual(str(db_net.id), api_net["id"])
86
        self.assertEqual(db_net.name, api_net['name'])
87
        if detail:
88
            self.assertEqual(db_net.state, api_net['status'])
89
            self.assertEqual(db_net.flavor, api_net['type'])
90
            self.assertEqual(db_net.subnet, api_net['cidr'])
91
            self.assertEqual(db_net.subnet6, api_net['cidr6'])
92
            self.assertEqual(db_net.gateway, api_net['gateway'])
93
            self.assertEqual(db_net.gateway6, api_net['gateway6'])
94
            self.assertEqual(db_net.dhcp, api_net['dhcp'])
95
            self.assertEqual(db_net.public, api_net['public'])
96
            db_nics = [nic.id for nic in
97
                       db_net.nics.filter(machine__userid=db_net.userid)]
98
            self.assertEqual(db_nics, api_net['attachments'])
99

  
100
    def test_create_network_1(self, mrapi):
101
        request = {
102
            'network': {'name': 'foo', "type": "MAC_FILTERED"}
103
        }
104
        with mocked_quotaholder():
105
            response = self.mypost('networks/', 'user1',
106
                                   json.dumps(request), 'json')
107
        self.assertEqual(response.status_code, 202)
108
        db_networks = Network.objects.filter(userid='user1')
109
        self.assertEqual(len(db_networks), 1)
110
        db_net = db_networks[0]
111
        api_net = json.loads(response.content)['network']
112
        self.assertNetworksEqual(db_net, api_net)
113
        mrapi.CreateNetwork.assert_called()
114
        mrapi.ConnectNetwork.assert_called()
115

  
116
    def test_invalid_data_1(self, mrapi):
117
        """Test invalid flavor"""
118
        request = {
119
            'network': {'name': 'foo', 'type': 'LoLo'}
120
            }
121
        response = self.mypost('networks/', 'user1',
122
                               json.dumps(request), 'json')
123
        self.assertBadRequest(response)
124
        self.assertEqual(len(Network.objects.filter(userid='user1')), 0)
125

  
126
    def test_invalid_data_2(self, mrapi):
127
        """Test invalid data/subnet"""
128
        request = {
129
            'network': {'name': 'foo',
130
                        'cidr': '10.0.0.0/8', "type":
131
                        "MAC_FILTERED"}
132
        }
133
        response = self.mypost('networks/', 'user1',
134
                               json.dumps(request), 'json')
135
        self.assertFault(response, 413, "overLimit")
136

  
137
    def test_invalid_data_3(self, mrapi):
138
        """Test unauthorized to create public network"""
139
        request = {
140
            'network': {"name": 'foo',
141
                        "public": "True",
142
                        "type": "MAC_FILTERED"}
143
            }
144
        response = self.mypost('networks/', 'user1',
145
                               json.dumps(request), 'json')
146
        self.assertFault(response, 403, "forbidden")
147

  
148
    def test_invalid_data_4(self, mrapi):
149
        """Test unauthorized to create network not in settings"""
150
        request = {
151
            'network': {'name': 'foo', 'type': 'CUSTOM'}
152
            }
153
        response = self.mypost('networks/', 'user1',
154
                               json.dumps(request), 'json')
155
        self.assertFault(response, 403, "forbidden")
156

  
157
    def test_invalid_subnet(self, mrapi):
158
        """Test invalid subnet"""
159
        request = {
160
            'network': {'name': 'foo',
161
                        'cidr': '10.0.0.10/27',
162
                        "type": "MAC_FILTERED"}
163
        }
164
        response = self.mypost('networks/', 'user1',
165
                               json.dumps(request), 'json')
166
        self.assertBadRequest(response)
167

  
168
    def test_invalid_gateway_1(self, mrapi):
169
        request = {
170
            'network': {'name': 'foo',
171
                        'cidr': '10.0.0.0/28',
172
                        'gateway': '10.0.0.0.300'}
173
        }
174
        response = self.mypost('networks/', 'user1',
175
                               json.dumps(request), 'json')
176
        self.assertBadRequest(response)
177

  
178
    def test_invalid_gateway_2(self, mrapi):
179
        request = {
180
            'network': {'name': 'foo',
181
                        'cidr': '10.0.0.0/28',
182
                        'gateway': '10.2.0.1'}
183
        }
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff