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