Revision 69dadbe4

b/snf-cyclades-app/synnefo/api/management/commands/floating-ip-dettach.py
37 37
from synnefo.management.common import get_floating_ip_by_address
38 38
from synnefo.logic import backend
39 39

  
40

  
40 41
class Command(BaseCommand):
41 42
    can_import_settings = True
42 43
    output_transaction = True
......
44 45
    help = "Dettach a floating IP from a VM or router"
45 46

  
46 47
    def handle(self, *args, **options):
47
        if not args or len(args)>1:
48
        if not args or len(args) > 1:
48 49
            raise CommandError("Command accepts exactly one argument")
49 50

  
50 51
        address = args[0]
b/snf-cyclades-app/synnefo/api/management/commands/floating-ip-release.py
39 39
from synnefo.management.common import get_floating_ip_by_address
40 40
from synnefo import quotas
41 41

  
42

  
42 43
class Command(BaseCommand):
43 44
    can_import_settings = True
44 45
    output_transaction = True
b/snf-cyclades-app/synnefo/api/management/commands/port-create.py
90 90
        security_groups = options['security-groups']
91 91

  
92 92
        if not name:
93
            name=""
93
            name = ""
94 94

  
95 95
        if (server and router) or not (server or router):
96 96
            raise CommandError('Please give either a server or a router id')
b/snf-cyclades-app/synnefo/api/management/commands/port-remove.py
31 31
from django.core.management.base import BaseCommand, CommandError
32 32
from synnefo.logic import ports
33 33
from synnefo.api.util import get_port
34
from synnefo.management.common  import convert_api_faults
34
from synnefo.management.common import convert_api_faults
35

  
35 36

  
36 37
class Command(BaseCommand):
37 38
    can_import_settings = True
b/snf-cyclades-app/synnefo/api/routers.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

  
34
from django.http import HttpResponse
35
from django.conf.urls import patterns
36
from django.utils import simplejson as json
37
from django.db import transaction
38
from django.db.models import Q
39
from django.conf import settings
40
from snf_django.lib import api
41
from snf_django.lib.api import utils
42
from django.template.loader import render_to_string
43
from synnefo.api import util
44

  
45
from synnefo.db.models import VirtualMachine, IPAddress, NetworkInterface
46
from logging import getLogger
47

  
48
from synnefo.logic import servers, backend
49

  
50
log = getLogger(__name__)
51

  
52
urlpatterns = patterns(
53
    'synnefo.api.routers',
54
    (r'^(?:/|.json|.xml)?$', 'demux'),
55
    (r'^/detail(?:.json|.xml)?$', 'list_routers', {'detail': True}),
56
    (r'^/([-\w]+)(?:/|.json|.xml)?$', 'router_demux'),
57
    (r'^/([-\w]+)/(remove_router_interface|add_router_interface)$',
58
        'rinterface_demux'))
59

  
60

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

  
69

  
70
def router_demux(request, offset):
71
    if request.method == 'GET':
72
        return get_router(request,  offset)
73
    elif request.method == 'DELETE':
74
        return delete_router(request, offset)
75
    elif request.method == 'PUT':
76
        return update_router(request, offset)
77
    else:
78
        return api.api_method_not_allowed(request)
79

  
80

  
81
def rinterface_demux(request, router_id, command):
82
    if request.method == 'PUT':
83
        if command == "add_router_interface":
84
            return add_interface(request, router_id)
85
        elif command == "remove_router_interface":
86
            return remove_interface(request, router_id)
87
    else:
88
        return api.api_method_not_allowed(request)
89

  
90

  
91
@api.api_method(http_method='GET', user_required=True, logger=log)
92
def list_routers(request, detail=False):
93
    log.debug('list_routers')
94

  
95
    user_routers = VirtualMachine.objects.filter(userid=request.user_uniq,
96
                                                 router=True)
97

  
98
    user_routers = utils.filter_modified_since(request, objects=user_routers)
99

  
100
    routers = [router_to_dict(router, detail)
101
               for router in user_routers.order_by('id')]
102

  
103
    if request.serialization == 'xml':
104
        data = render_to_string('list_routers.xml', {
105
            'routers': routers})
106
    else:
107
        data = json.dumps({"routers": routers})
108

  
109
    return HttpResponse(data, status=200)
110

  
111

  
112
@api.api_method(http_method='POST', user_required=True, logger=log)
113
def create_router(request):
114
    user_id = request.user_uniq
115
    req = utils.get_request_dict(request)
116

  
117
    info = api.utils.get_attribute(req, 'router', required=True)
118
    gateway_info = api.utils.get_attribute(info, 'external_gateway_info',
119
                                            required=True)
120
    net_id = api.utils.get_attribute(gateway_info, 'network_id', required=True)
121

  
122
    #find the network
123
    network = util.get_network(net_id, request.user_uniq)
124

  
125
    fip_address = api.utils.get_attribute(gateway_info, 'floating_ip',
126
                                          required=False)
127
    if fip_address:
128
        #find the floating ip
129
        floating_ip = util.get_floating_ip_by_address(user_id, fip_address,
130
                                                      for_update=True)
131
        if floating_ip.network.id != network.id:
132
            msg = "The ip is not on the given network"
133
            raise api.faults.Conflict(msg)
134
    else:
135
        #assuming that the floating-ips are preallocated
136
        fips = IPAddress.objects.filter(userid=user_id,
137
                                               network=network,
138
                                               nic=None,
139
                                               floating_ip=True)
140
        if fips:
141
            floating_ip = fips[0]
142
        else:
143
            # FIXME return correct code
144
            raise api.faults.BadRequest('No available floating ips')
145

  
146
    name = api.utils.get_attribute(info, 'name', required=False)
147
    if not name:
148
        name = 'random-router'
149

  
150
    #FIXME : double query for floating-ip
151
    #create the router
152
    vm = servers.create(user_id,
153
                        name,
154
                        'password',
155
                        'flavor',
156
                        'image',
157
                        floating_ips=[floating_ip.address])
158

  
159
    response = render_router(request, router_to_dict(vm, detail=True),
160
                             status=201)
161

  
162
    return response
163

  
164

  
165
@api.api_method(http_method='GET', user_required=True, logger=log)
166
def get_router(request, router_id):
167
    log.debug('get_router_details %s', router_id)
168
    router = util.get_vm(router_id, request.user_uniq)
169

  
170
    router_dict = router_to_dict(router, detail=True)
171
    return render_router(request, router_dict)
172

  
173

  
174
@api.api_method(http_method='DELETE', user_required=True, logger=log)
175
def delete_router(request, router_id):
176
    log.debug('delete router  %s', router_id)
177
    router = util.get_vm(router_id, request.user_uniq)
178

  
179
    if router.networks.filter(external_router=False):
180
        return HttpResponse("There are internal interfaces on the router",
181
                            status=409)
182

  
183
    servers.destroy(router)
184

  
185
    return HttpResponse(status=204)
186

  
187

  
188
@api.api_method(http_method='PUT', user_required=True, logger=log)
189
def update_router(request, router_id):
190
    """ Update only name and external_gateway_info"""
191

  
192
    log.debug('update router %s', router_id)
193
    router = util.get_vm(router_id, request.user_uniq)
194
    user_id = request.user_uniq
195
    req = utils.get_request_dict(request)
196

  
197
    info = api.utils.get_attribute(req, 'router', required=True)
198
    gateway = api.utils.get_attribute(info, 'external_gateway_info',
199
                                      required=False)
200
    if gateway:
201
        net_id = api.utils.get_attribute(gateway, 'network_id', required=True)
202
        fip_address = api.utils.get_attribute(gateway, 'floating_ip',
203
                                              required=False)
204

  
205
    # rename if name given
206
    name = api.utils.get_attribute(info, 'name', required=False)
207
    if name:
208
        servers.rename(router, name)
209

  
210
    if gateway:
211
        #find the new network
212
        network = util.get_network(net_id, request.user_uniq)
213

  
214
        if fip_address:
215
            #find the floating ip
216
            floating_ip = util.get_floating_ip_by_address(user_id, fip_address,
217
                                                          for_update=True)
218
            if floating_ip.network.id != network.id:
219
                msg = "The ip is not on the given network"
220
                raise api.faults.Conflict(msg)
221
        else:
222
            #assuming that the floating-ips are preallocated
223
            fips = IPAddress.objects.filter(userid=user_id,
224
                                                   network=network,
225
                                                   nic=None,
226
                                                   floating_ip=True)
227
            if fips:
228
                floating_ip = fips[0]
229
            else:
230
                # FIXME return correct code
231
                raise api.faults.BadRequest('No available floating ips')
232

  
233
        #disconnect from the old net if any
234
        try:
235
            old_nic = router.nics.get(network__external_router=True)
236
        except NetworkInterface.DoesNotExist:
237
            old_nic = None
238

  
239
        if old_nic:
240
                servers.disconnect(router, old_nic)
241

  
242
        #add the new floating-ip
243
        servers.create_nic(router, ipaddress=floating_ip)
244

  
245
    routerdict = router_to_dict(router)
246
    return render_router(request, routerdict, 200)
247

  
248

  
249
@api.api_method(http_method='PUT', user_required=True, logger=log)
250
def add_interface(request, router_id):
251
    log.debug('add interface to router %s', router_id)
252
    info = utils.get_request_dict(request)
253
    router = util.get_vm(router_id, request.user_uniq)
254

  
255
    subnet_id = api.utils.get_attribute(info, "subnet_id", required=True)
256
    subnet = util.get_subnet(subnet_id, request.user_uniq, public=False)
257

  
258
    if subnet.ipversion != 4:
259
        raise api.faults.BadRequest("IPv4 subnet needed")
260

  
261
    #create nic
262
    nic, ippaddress = servers.create_nic(router, subnet.network,
263
                                         address=subnet.gateway)
264
    #connect
265
    backend.connect(router, nic)
266

  
267
    res = {"port_id": str(nic.id),
268
           "subnet_id": subnet.id}
269

  
270
    data = json.dumps(res)
271
    return HttpResponse(data, status=200)
272

  
273

  
274
@api.api_method(http_method='PUT', user_required=True, logger=log)
275
def remove_interface(request, router_id):
276

  
277
    log.debug('remove interface from router %s', router_id)
278

  
279
    router = util.get_vm(router_id, request.user_uniq)
280

  
281
    info = utils.get_request_dict(request)
282

  
283
    subnet_id = api.utils.get_attribute(info, "subnet_id", required=True)
284
    subnet = util.get_subnet(subnet_id, request.user_uniq, public=False)
285

  
286
    #get the port
287
    try:
288
        port = router.nics.get(network=subnet)
289
    except (ValueError, NetworkInterface.DoesNotExist):
290
        raise api.faults.ItemNotFound('Port not found')
291

  
292
    res = {"id": str(router.id),
293
           "tenant_id": request.user_uniq,
294
           "port_id": str(port.id),
295
           "subnet_id": str(subnet.id)}
296

  
297
    #disconnect
298
    servers.disconnect(router, port)
299

  
300
    data = json.dumps(res)
301
    return HttpResponse(data, status=200)
302

  
303

  
304
# util functions
305

  
306

  
307
def router_to_dict(router, detail=True):
308
    d = {'id': str(router.id), 'name': router.name}
309
    d['user_id'] = router.userid
310
    d['tenant_id'] = router.userid
311
    d['admin_state_up'] = True
312
    if detail:
313
        external_nic = router.nics.get(network__external_router=True)
314
        if external_nic:
315
            fip = external_nic.ips.get(floating_ip=True)
316
            d['external_gateway_info'] = {'network_id':
317
                                            str(external_nic.network.id),
318
                                          'floating_ip_id': fip.address}
319
        else:
320
            d['external_gateway_info'] = None
321
    return d
322

  
323

  
324
def render_router(request, routerdict, status=200):
325
    if request.serialization == 'xml':
326
        data = render_to_string('router.xml', {'router': routerdict})
327
    else:
328
        data = json.dumps({'router': routerdict})
329
    return HttpResponse(data, status=status)
330

  
331

  
332
# mock functions
333

  
334
def mock_disconnect(router, net):
335
    nic = models.NetworkInterface.objects.get(network=net, machine=router)
336
    nic.delete()
337

  
338

  
339
def mock_connect(router, net, fip):
340
    fip.machine = router
341
    nic = models.NetworkInterface.objects.create(network=net,
342
                                                 machine=router,
343
                                                 ipv4=fip.ipv4,
344
                                                 owner="router")
345
    nic.save()
346
    fip.save()
b/snf-cyclades-app/synnefo/api/tests/__init__.py
40 40
from .versions import *
41 41
from .extensions import *
42 42
from .subnets import *
43
from .routers import *
43 44
from .floating_ips import *
b/snf-cyclades-app/synnefo/api/tests/routers.py
1
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from snf_django.utils.testing import BaseAPITest
35
from django.utils import simplejson as json
36
from synnefo.cyclades_settings import cyclades_services
37
from synnefo.lib.services import get_service_path
38
from synnefo.lib import join_urls
39
import synnefo.db.models_factory as dbmf
40

  
41

  
42
COMPUTE_URL = get_service_path(cyclades_services, 'compute',
43
                               version='v2.0')
44
ROUTERS_URL = join_urls(COMPUTE_URL, "routers/")
45

  
46

  
47
class RouterTest(BaseAPITest):
48

  
49
    def test_list_empty_routers(self):
50
        response = self.get(ROUTERS_URL)
51
        self.assertSuccess(response)
52
        routers = json.loads(response.content)
53
        self.assertEqual(routers, {"routers": []})
54

  
55
    def test_list_detail(self):
56
        response = self.get(join_urls(ROUTERS_URL, 'detail'))
57
        self.assertEqual(response.status_code, 200)
58

  
59
    def test_create_router_no_net(self):
60
        request = {
61
            "router": {
62
                "name": "test-router",
63
                "external_gateway_info": {
64
                    "network_id": "123"}
65
            }
66
        }
67
        response = self.post(ROUTERS_URL, params=json.dumps(request),
68
                             user='user')
69
        self.assertEqual(response.status_code, 404)
70

  
71
    def test_create_router_no_external(self):
72
        net = dbmf.NetworkFactory.create(userid='user', external_router=False)
73
        request = {
74
            "router": {
75
                "name": "test-router",
76
                "external_gateway_info": {
77
                    "network_id": str(net.id)
78
                }
79
            }
80
        }
81
        response = self.post(ROUTERS_URL, params=json.dumps(request),
82
                             user='user')
83
        self.assertEqual(response.status_code, 400)
84

  
85
    def test_create_router(self):
86
        try:
87
            flavor = dbmf.FlavorFactory.create(id=1)
88
        except:
89
            pass
90
        net = dbmf.NetworkFactory.create(userid='user', external_router=True)
91
        fip = dbmf.FloatingIPFactory.create(userid='user',
92
                                          network=net, nic=None)
93
        request = {
94
            "router": {
95
                "name": "test-router",
96
                "external_gateway_info": {
97
                    "network_id": str(fip.network.id),
98
                    "floating_ip": fip.address}
99
            }
100
        }
101
        response = self.post(ROUTERS_URL, params=json.dumps(request),
102
                             user='user')
103
        self.assertEqual(response.status_code, 201)
104

  
105
    def test_create_router_no_ip(self):
106
        try:
107
            flavor = dbmf.FlavorFactory.create(id=1)
108
        except:
109
            pass
110
        net = dbmf.NetworkFactory.create(userid='user', external_router=True)
111
        fip = dbmf.FloatingIPFactory.create(userid='user',
112
                                          network=net, nic=None)
113
        request = {
114
            "router": {
115
                "name": "test-router",
116
                "external_gateway_info": {
117
                    "network_id": str(net.id)}
118
            }
119
        }
120
        response = self.post(ROUTERS_URL, params=json.dumps(request),
121
                             user='user')
122
        self.assertEqual(response.status_code, 201)
123

  
124
    def test_get_router(self):
125
        router = dbmf.VirtualMachineFactory.create(router=True)
126
        nic = dbmf.NetworkInterfaceFactory.create(machine=router)
127
        fip = dbmf.FloatingIPFactory.create(userid=router.userid, nic=nic,
128
                                            network=nic.network)
129
        response = self.get(ROUTERS_URL, user=router.userid)
130
        self.assertSuccess(response)
131

  
132
    def test_delete_router(self):
133
        router = dbmf.VirtualMachineFactory.create(router=True)
134
        url = join_urls(ROUTERS_URL, str(router.id))
135
        response = self.delete(url, user=router.userid)
136
        self.assertEqual(response.status_code, 204)
137

  
138
    def test_delete_router_with_private_net(self):
139
        router = dbmf.VirtualMachineFactory.create(router=True)
140
        net = dbmf.NetworkFactory.create(external_router=False)
141
        nic = dbmf.NetworkInterfaceFactory.create(network=net, machine=router)
142
        url = join_urls(ROUTERS_URL, str(router.id))
143
        response = self.delete(url, user=router.userid)
144
        self.assertEqual(response.status_code, 409)
145

  
146
    def test_update_router_network(self):
147
        router = dbmf.VirtualMachineFactory.create(router=True, userid='user')
148
        net = dbmf.NetworkFactory.create(userid='user', external_router=True)
149
        fip = dbmf.FloatingIPFactory.create(userid='user',
150
                                          network=net, nic=None)
151
        request = {
152
            "router": {
153
                "name": "new_name",
154
                "external_gateway_info": {"network_id": str(net.id)}
155
                }
156
            }
157
        url = join_urls(ROUTERS_URL, str(router.id))
158
        response = self.put(url, params=json.dumps(request),
159
                            user=router.userid)
160
        info = json.loads(response.content)
161
        self.assertEqual(info['router']['external_gateway_info']['network_id'],
162
                         str(net.id))
163
        self.assertEqual(info['router']['name'], "new_name")
164

  
165
    def test_update_router_both(self):
166
        router = dbmf.VirtualMachineFactory.create(router=True, userid='user')
167
        net = dbmf.NetworkFactory.create(userid='user', external_router=True)
168
        fip = dbmf.FloatingIPFactory.create(userid='user',
169
                                          network=net, nic=None)
170
        request = {
171
            "router": {
172
                "name": "new_name",
173
                "external_gateway_info": {"network_id": str(net.id),
174
                                           "floating_ip": fip.address}
175
                }
176
            }
177
        url = join_urls(ROUTERS_URL, str(router.id))
178
        response = self.put(url, params=json.dumps(request),
179
                            user=router.userid)
180
        info = json.loads(response.content)
181
        self.assertEqual(info['router']['external_gateway_info']['network_id'],
182
                         str(net.id))
183
        self.assertEqual(info['router']['name'], "new_name")
184

  
185
    def test_update_router_conflict(self):
186
        router = dbmf.VirtualMachineFactory.create(router=True, userid='user')
187
        net = dbmf.NetworkFactory.create(userid='user', external_router=True)
188
        fip = dbmf.FloatingIPFactory.create(userid='user', nic=None)
189
        request = {
190
            "router": {
191
                "name": "new_name",
192
                "external_gateway_info": {"network_id": str(net.id),
193
                                           "floating_ip": fip.address}
194
                }
195
            }
196
        url = join_urls(ROUTERS_URL, str(router.id))
197
        response = self.put(url, params=json.dumps(request),
198
                            user=router.userid)
199
        self.assertEqual(response.status_code, 409)
200

  
201
    def test_remove_interface_no_body(self):
202
        url = join_urls(join_urls(ROUTERS_URL, "123"),
203
                        "remove_router_interface")
204

  
205
        response = self.put(url, params="")
206
        self.assertEqual(response.status_code, 400)
207

  
208
    def test_remove_interface_unfound_subnet(self):
209
        router = dbmf.VirtualMachineFactory.create(router=True)
210
        request = {"subnet_id": "123"}
211
        url = join_urls(join_urls(ROUTERS_URL, str(router.id)),
212
                        "remove_router_interface")
213
        response = self.put(url, params=json.dumps(request),
214
                            user=router.userid)
215
        self.assertEqual(response.status_code, 404)
216

  
217
    def test_remove_interface_no_info(self):
218
        router = dbmf.VirtualMachineFactory.create(router=True)
219
        request = {"wrong": "123"}
220
        url = join_urls(join_urls(ROUTERS_URL, str(router.id)),
221
                        "remove_router_interface")
222
        response = self.put(url, params=json.dumps(request),
223
                            user=router.userid)
224
        self.assertEqual(response.status_code, 400)
225

  
226
    def test_remove_interface_subnet(self):
227
        router = dbmf.VirtualMachineFactory.create(router=True)
228
        net1 = dbmf.NetworkFactory.create(external_router=True,
229
                                          userid=router.userid)
230
        subnet = dbmf.SubnetFactory.create(network=net1)
231
        nic = dbmf.NetworkInterfaceFactory.create(network=net1, machine=router)
232
        request = {"subnet_id": subnet.id}
233
        url = join_urls(join_urls(ROUTERS_URL, str(router.id)),
234
                        "remove_router_interface")
235
        response = self.put(url, params=json.dumps(request),
236
                            user=router.userid)
237
        self.assertEqual(response.status_code, 200)
238

  
239
    def test_add_interface_no_info(self):
240
        url = join_urls(join_urls(ROUTERS_URL, "123"), "add_router_interface")
241
        response = self.put(url, params="")
242
        self.assertEqual(response.status_code, 400)
243

  
244
    def test_add_interface_wrong_info(self):
245
        router = dbmf.VirtualMachineFactory.create(router=True)
246
        url = join_urls(join_urls(ROUTERS_URL, str(router.id)),
247
                        "add_router_interface")
248
        request = {}
249
        response = self.put(url, params=json.dumps(request),
250
                            user=router.userid)
251
        self.assertEqual(response.status_code, 400)
252

  
253
    def test_add_interface_unfound_subnet(self):
254
        router = dbmf.VirtualMachineFactory.create(router=True)
255
        url = join_urls(join_urls(ROUTERS_URL, str(router.id)),
256
                        "add_router_interface")
257
        request = {"subnet_id": "123"}
258
        response = self.put(url, params=json.dumps(request),
259
                            user=router.userid)
260
        self.assertEqual(response.status_code, 404)
261

  
262
    def test_add_interface_subnet(self):
263
        router = dbmf.VirtualMachineFactory.create(router=True)
264
        net1 = dbmf.NetworkFactory.create(external_router=True,
265
                                          userid=router.userid)
266
        subnet = dbmf.SubnetFactory.create(network=net1, ipversion=4)
267
        url = join_urls(join_urls(ROUTERS_URL, str(router.id)),
268
                        "add_router_interface")
269
        request = {"subnet_id": subnet.id}
270
        response = self.put(url, params=json.dumps(request),
271
                            user=router.userid)
272
        self.assertEqual(response.status_code, 200)
b/snf-cyclades-app/synnefo/api/urls.py
35 35

  
36 36
from snf_django.lib.api import api_endpoint_not_found
37 37
from synnefo.api import (servers, flavors, images, networks, extensions,
38
                         ports, floating_ips, subnets)
38
                         ports, floating_ips, subnets, routers)
39 39
from synnefo.api.versions import versions_list, version_details
40 40

  
41 41

  
......
50 50
    (r'^networks', include(networks)),
51 51
    (r'^ports', include(ports)),
52 52
    (r'^subnets', include(subnets)),
53
    (r'^routers', include(routers)),
53 54
    (r'^extensions', include(extensions)),
54 55
    (r'^os-floating-ips', include(floating_ips.ips_urlpatterns)),
55 56
    (r'^os-floating-ip-pools', include(floating_ips.pools_urlpatterns)),
b/snf-cyclades-app/synnefo/api/util.py
49 49
from synnefo.db.models import (Flavor, VirtualMachine, VirtualMachineMetadata,
50 50
                               Network, NetworkInterface, SecurityGroup,
51 51
                               BridgePoolTable, MacPrefixPoolTable, IPAddress,
52
                               IPPoolTable)
52
                               IPPoolTable, Subnet)
53 53
from synnefo.db import pools
54 54

  
55 55
from synnefo.plankton.utils import image_backend
......
226 226
        raise faults.ItemNotFound('Network %s not found.' % network_id)
227 227

  
228 228

  
229
def get_subnet(subnet_id, user_id, for_update=False, public=True,
230
               non_deleted=False):
231
    """Return a Subnet instance or raise ItemNotFound."""
232

  
233
    try:
234
        subnet_id = int(subnet_id)
235
        objects = Subnet.objects
236
        if for_update:
237
            objects = objects.select_for_update()
238
        if public:
239
            subnet = objects.get(Q(network__userid=user_id) |
240
                                 Q(network__public=True), id=subnet_id)
241
        else:
242
            subnet = objects.get(network__userid=user_id, id=subnet_id)
243
        if non_deleted and subnet.deleted:
244
            raise faults.BadRequest("Subnet has been deleted.")
245
        return subnet
246
    except (ValueError, Subnet.DoesNotExist):
247
        raise faults.ItemNotFound('Subnet %s not found.' % subnet_id)
248

  
249

  
229 250
def get_port(port_id, user_id, for_update=False):
230 251
    """
231 252
    Return a NetworkInteface instance or raise ItemNotFound.
......
247 268
    except (ValueError, NetworkInterface.DoesNotExist):
248 269
        raise faults.ItemNotFound('Port not found.')
249 270

  
271

  
250 272
def get_security_group(sg_id):
251 273
    try:
252 274
        sg = SecurityGroup.objects.get(id=sg_id)
......
254 276
    except (ValueError, SecurityGroup.DoesNotExist):
255 277
        raise faults.ItemNotFound("Not valid security group")
256 278

  
279

  
257 280
def get_floating_ip_by_address(userid, address, for_update=False):
258 281
    try:
259 282
        objects = IPAddress.objects
b/snf-cyclades-app/synnefo/db/models.py
492 492
    state = models.CharField(choices=OPER_STATES, max_length=32,
493 493
                             default='PENDING')
494 494
    machines = models.ManyToManyField(VirtualMachine,
495
                                      through='NetworkInterface')
495
                                      through='NetworkInterface',
496
                                      related_name='networks')
496 497
    action = models.CharField(choices=ACTIONS, max_length=32, null=True,
497 498
                              default=None)
498 499
    drained = models.BooleanField("Drained", default=False, null=False)
......
956 957

  
957 958
    class Meta:
958 959
        ordering = ['-created']
960
# Copyright 2011-2012 GRNET S.A. All rights reserved.

Also available in: Unified diff