root / snf-cyclades-app / synnefo / api / ports.py @ ba777b02
History | View | Annotate | Download (9.6 kB)
1 | 5db2001a | Marios Kogias | # Copyright 2011-2013 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | 5db2001a | Marios Kogias | #
|
3 | 5db2001a | Marios Kogias | # Redistribution and use in source and binary forms, with or
|
4 | 5db2001a | Marios Kogias | # without modification, are permitted provided that the following
|
5 | 5db2001a | Marios Kogias | # conditions are met:
|
6 | 5db2001a | Marios Kogias | #
|
7 | 5db2001a | Marios Kogias | # 1. Redistributions of source code must retain the above
|
8 | 5db2001a | Marios Kogias | # copyright notice, this list of conditions and the following
|
9 | 5db2001a | Marios Kogias | # disclaimer.
|
10 | 5db2001a | Marios Kogias | #
|
11 | 5db2001a | Marios Kogias | # 2. Redistributions in binary form must reproduce the above
|
12 | 5db2001a | Marios Kogias | # copyright notice, this list of conditions and the following
|
13 | 5db2001a | Marios Kogias | # disclaimer in the documentation and/or other materials
|
14 | 5db2001a | Marios Kogias | # provided with the distribution.
|
15 | 5db2001a | Marios Kogias | #
|
16 | 5db2001a | Marios Kogias | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | 5db2001a | Marios Kogias | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | 5db2001a | Marios Kogias | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | 5db2001a | Marios Kogias | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | 5db2001a | Marios Kogias | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | 5db2001a | Marios Kogias | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | 5db2001a | Marios Kogias | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | 5db2001a | Marios Kogias | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | 5db2001a | Marios Kogias | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | 5db2001a | Marios Kogias | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | 5db2001a | Marios Kogias | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | 5db2001a | Marios Kogias | # POSSIBILITY OF SUCH DAMAGE.
|
28 | 5db2001a | Marios Kogias | #
|
29 | 5db2001a | Marios Kogias | # The views and conclusions contained in the software and
|
30 | 5db2001a | Marios Kogias | # documentation are those of the authors and should not be
|
31 | 5db2001a | Marios Kogias | # interpreted as representing official policies, either expressed
|
32 | 5db2001a | Marios Kogias | # or implied, of GRNET S.A.
|
33 | 5db2001a | Marios Kogias | |
34 | 86961519 | Christos Stavrakakis | #from django.conf import settings
|
35 | 69c8d65d | Christos Stavrakakis | import ipaddr |
36 | 593851e0 | Buildbot | from django.conf.urls import patterns |
37 | 593851e0 | Buildbot | from django.http import HttpResponse |
38 | 593851e0 | Buildbot | from django.utils import simplejson as json |
39 | 593851e0 | Buildbot | from django.db import transaction |
40 | 593851e0 | Buildbot | from django.template.loader import render_to_string |
41 | 5db2001a | Marios Kogias | |
42 | 5db2001a | Marios Kogias | from snf_django.lib import api |
43 | 69c8d65d | Christos Stavrakakis | from snf_django.lib.api import faults |
44 | 5db2001a | Marios Kogias | |
45 | 593851e0 | Buildbot | from synnefo.api import util |
46 | 86961519 | Christos Stavrakakis | from synnefo.db.models import NetworkInterface |
47 | 0292883e | Christos Stavrakakis | from synnefo.logic import servers, ips |
48 | 593851e0 | Buildbot | |
49 | 593851e0 | Buildbot | from logging import getLogger |
50 | 593851e0 | Buildbot | |
51 | 593851e0 | Buildbot | log = getLogger(__name__) |
52 | 593851e0 | Buildbot | |
53 | 593851e0 | Buildbot | urlpatterns = patterns( |
54 | 593851e0 | Buildbot | 'synnefo.api.ports',
|
55 | 593851e0 | Buildbot | (r'^(?:/|.json|.xml)?$', 'demux'), |
56 | 342acb45 | Kostas Papadimitriou | (r'^/detail(?:.json|.xml)?$', 'list_ports', {'detail': True}), |
57 | 593851e0 | Buildbot | (r'^/([-\w]+)(?:/|.json|.xml)?$', 'port_demux')) |
58 | 593851e0 | Buildbot | |
59 | 5db2001a | Marios Kogias | |
60 | 593851e0 | Buildbot | def demux(request): |
61 | 593851e0 | Buildbot | if request.method == 'GET': |
62 | 593851e0 | Buildbot | return list_ports(request)
|
63 | 593851e0 | Buildbot | elif request.method == 'POST': |
64 | 593851e0 | Buildbot | return create_port(request)
|
65 | 593851e0 | Buildbot | else:
|
66 | 593851e0 | Buildbot | return api.api_method_not_allowed(request)
|
67 | 593851e0 | Buildbot | |
68 | 593851e0 | Buildbot | |
69 | 5db2001a | Marios Kogias | def port_demux(request, port_id): |
70 | 593851e0 | Buildbot | |
71 | 593851e0 | Buildbot | if request.method == 'GET': |
72 | 5db2001a | Marios Kogias | return get_port_details(request, port_id)
|
73 | 593851e0 | Buildbot | elif request.method == 'DELETE': |
74 | 5db2001a | Marios Kogias | return delete_port(request, port_id)
|
75 | 593851e0 | Buildbot | elif request.method == 'PUT': |
76 | 5db2001a | Marios Kogias | return update_port(request, port_id)
|
77 | 593851e0 | Buildbot | else:
|
78 | 593851e0 | Buildbot | return api.api_method_not_allowed(request)
|
79 | 593851e0 | Buildbot | |
80 | 593851e0 | Buildbot | |
81 | 593851e0 | Buildbot | @api.api_method(http_method='GET', user_required=True, logger=log) |
82 | bf58eed3 | Dionysis Grigoropoulos | def list_ports(request, detail=True): |
83 | 593851e0 | Buildbot | |
84 | 593851e0 | Buildbot | log.debug('list_ports detail=%s', detail)
|
85 | 593851e0 | Buildbot | |
86 | 3549cb2f | Dionysis Grigoropoulos | user_ports = NetworkInterface.objects.filter(userid=request.user_uniq) |
87 | 593851e0 | Buildbot | |
88 | 883c1f94 | Christos Stavrakakis | if detail:
|
89 | 883c1f94 | Christos Stavrakakis | user_ports = user_ports.prefetch_related("ips")
|
90 | 883c1f94 | Christos Stavrakakis | |
91 | 5db2001a | Marios Kogias | port_dicts = [port_to_dict(port, detail) |
92 | 9f6760ee | Christos Stavrakakis | for port in user_ports.order_by('id')] |
93 | 593851e0 | Buildbot | |
94 | 593851e0 | Buildbot | if request.serialization == 'xml': |
95 | 5db2001a | Marios Kogias | data = render_to_string('list_ports.xml', {
|
96 | 5db2001a | Marios Kogias | "ports": port_dicts})
|
97 | 593851e0 | Buildbot | else:
|
98 | 5db2001a | Marios Kogias | data = json.dumps({'ports': port_dicts})
|
99 | 593851e0 | Buildbot | |
100 | 593851e0 | Buildbot | return HttpResponse(data, status=200) |
101 | 593851e0 | Buildbot | |
102 | 593851e0 | Buildbot | |
103 | 593851e0 | Buildbot | @api.api_method(http_method='POST', user_required=True, logger=log) |
104 | fae6e5f0 | Christos Stavrakakis | @transaction.commit_on_success
|
105 | 593851e0 | Buildbot | def create_port(request): |
106 | 593851e0 | Buildbot | user_id = request.user_uniq |
107 | 5db2001a | Marios Kogias | req = api.utils.get_request_dict(request) |
108 | 593851e0 | Buildbot | log.info('create_port %s', req)
|
109 | 593851e0 | Buildbot | |
110 | f16aa9e6 | Marios Kogias | port_dict = api.utils.get_attribute(req, "port")
|
111 | f16aa9e6 | Marios Kogias | net_id = api.utils.get_attribute(port_dict, "network_id")
|
112 | f16aa9e6 | Marios Kogias | |
113 | e1fab40f | Christos Stavrakakis | device_id = api.utils.get_attribute(port_dict, "device_id", required=False) |
114 | e1fab40f | Christos Stavrakakis | vm = None
|
115 | e1fab40f | Christos Stavrakakis | if device_id is not None: |
116 | e1fab40f | Christos Stavrakakis | vm = util.get_vm(device_id, user_id, for_update=True, non_deleted=True, |
117 | e1fab40f | Christos Stavrakakis | non_suspended=True)
|
118 | f16aa9e6 | Marios Kogias | |
119 | 69c8d65d | Christos Stavrakakis | # Check if the request contains a valid IPv4 address
|
120 | 69c8d65d | Christos Stavrakakis | fixed_ips = api.utils.get_attribute(port_dict, "fixed_ips", required=False) |
121 | 69c8d65d | Christos Stavrakakis | if fixed_ips is not None and len(fixed_ips) > 0: |
122 | 69c8d65d | Christos Stavrakakis | if len(fixed_ips) > 1: |
123 | 69c8d65d | Christos Stavrakakis | msg = "'fixed_ips' attribute must contain only one fixed IP."
|
124 | 69c8d65d | Christos Stavrakakis | raise faults.BadRequest(msg)
|
125 | 69c8d65d | Christos Stavrakakis | fixed_ip_address = fixed_ips[0].get("ip_address") |
126 | 69c8d65d | Christos Stavrakakis | if fixed_ip_address is not None: |
127 | 69c8d65d | Christos Stavrakakis | try:
|
128 | 69c8d65d | Christos Stavrakakis | ip = ipaddr.IPAddress(fixed_ip_address) |
129 | 69c8d65d | Christos Stavrakakis | if ip.version == 6: |
130 | 69c8d65d | Christos Stavrakakis | msg = "'ip_address' can be only an IPv4 address'"
|
131 | 69c8d65d | Christos Stavrakakis | raise faults.BadRequest(msg)
|
132 | 69c8d65d | Christos Stavrakakis | except ValueError: |
133 | 69c8d65d | Christos Stavrakakis | msg = "%s is not a valid IPv4 Address" % fixed_ip_address
|
134 | 69c8d65d | Christos Stavrakakis | raise faults.BadRequest(msg)
|
135 | 69c8d65d | Christos Stavrakakis | else:
|
136 | 69c8d65d | Christos Stavrakakis | fixed_ip_address = None
|
137 | 69c8d65d | Christos Stavrakakis | |
138 | e1fab40f | Christos Stavrakakis | network = util.get_network(net_id, user_id, non_deleted=True,
|
139 | e1fab40f | Christos Stavrakakis | for_update=True)
|
140 | e1fab40f | Christos Stavrakakis | |
141 | 9c74cc19 | Marios Kogias | ipaddress = None
|
142 | f16aa9e6 | Marios Kogias | if network.public:
|
143 | 69c8d65d | Christos Stavrakakis | # Creating a port to a public network is only allowed if the user has
|
144 | 69c8d65d | Christos Stavrakakis | # already a floating IP address in this network which is specified
|
145 | 69c8d65d | Christos Stavrakakis | # as the fixed IP address of the port
|
146 | 69c8d65d | Christos Stavrakakis | if fixed_ip_address is None: |
147 | 69c8d65d | Christos Stavrakakis | msg = ("'fixed_ips' attribute must contain a floating IP address"
|
148 | 69c8d65d | Christos Stavrakakis | " in order to connect to a public network.")
|
149 | 69c8d65d | Christos Stavrakakis | raise faults.BadRequest(msg)
|
150 | 69c8d65d | Christos Stavrakakis | ipaddress = util.get_floating_ip_by_address(user_id, fixed_ip_address, |
151 | b968e8e6 | Christos Stavrakakis | for_update=True)
|
152 | 69c8d65d | Christos Stavrakakis | elif fixed_ip_address:
|
153 | 0292883e | Christos Stavrakakis | ipaddress = ips.allocate_ip(network, user_id, |
154 | 0292883e | Christos Stavrakakis | address=fixed_ip_address) |
155 | f16aa9e6 | Marios Kogias | |
156 | f16aa9e6 | Marios Kogias | name = api.utils.get_attribute(port_dict, "name", required=False) |
157 | f16aa9e6 | Marios Kogias | if name is None: |
158 | f16aa9e6 | Marios Kogias | name = ""
|
159 | f16aa9e6 | Marios Kogias | |
160 | f16aa9e6 | Marios Kogias | security_groups = api.utils.get_attribute(port_dict, |
161 | f16aa9e6 | Marios Kogias | "security_groups",
|
162 | f16aa9e6 | Marios Kogias | required=False)
|
163 | f16aa9e6 | Marios Kogias | #validate security groups
|
164 | f16aa9e6 | Marios Kogias | # like get security group from db
|
165 | a1713485 | Marios Kogias | sg_list = [] |
166 | f16aa9e6 | Marios Kogias | if security_groups:
|
167 | f16aa9e6 | Marios Kogias | for gid in security_groups: |
168 | a1713485 | Marios Kogias | sg = util.get_security_group(int(gid))
|
169 | a1713485 | Marios Kogias | sg_list.append(sg) |
170 | f16aa9e6 | Marios Kogias | |
171 | fae6e5f0 | Christos Stavrakakis | new_port = servers.create_port(user_id, network, use_ipaddress=ipaddress, |
172 | e0b9a688 | Dionysis Grigoropoulos | machine=vm, name=name) |
173 | 593851e0 | Buildbot | |
174 | 5db2001a | Marios Kogias | response = render_port(request, port_to_dict(new_port), status=201)
|
175 | 593851e0 | Buildbot | |
176 | 593851e0 | Buildbot | return response
|
177 | 593851e0 | Buildbot | |
178 | 593851e0 | Buildbot | |
179 | 5db2001a | Marios Kogias | @api.api_method(http_method='GET', user_required=True, logger=log) |
180 | 5db2001a | Marios Kogias | def get_port_details(request, port_id): |
181 | 5db2001a | Marios Kogias | log.debug('get_port_details %s', port_id)
|
182 | 5db2001a | Marios Kogias | port = util.get_port(port_id, request.user_uniq) |
183 | 5db2001a | Marios Kogias | return render_port(request, port_to_dict(port))
|
184 | 5db2001a | Marios Kogias | |
185 | 5db2001a | Marios Kogias | |
186 | 5db2001a | Marios Kogias | @api.api_method(http_method='PUT', user_required=True, logger=log) |
187 | 5db2001a | Marios Kogias | def update_port(request, port_id): |
188 | 5db2001a | Marios Kogias | '''
|
189 | 5db2001a | Marios Kogias | You can update only name, security_groups
|
190 | 5db2001a | Marios Kogias | '''
|
191 | 5db2001a | Marios Kogias | port = util.get_port(port_id, request.user_uniq, for_update=True)
|
192 | 5db2001a | Marios Kogias | req = api.utils.get_request_dict(request) |
193 | 5db2001a | Marios Kogias | |
194 | 5db2001a | Marios Kogias | port_info = api.utils.get_attribute(req, "port", required=True) |
195 | 5db2001a | Marios Kogias | name = api.utils.get_attribute(port_info, "name", required=False) |
196 | 5db2001a | Marios Kogias | |
197 | 5db2001a | Marios Kogias | if name:
|
198 | 5db2001a | Marios Kogias | port.name = name |
199 | 5db2001a | Marios Kogias | |
200 | 5db2001a | Marios Kogias | security_groups = api.utils.get_attribute(port_info, "security_groups",
|
201 | 5db2001a | Marios Kogias | required=False)
|
202 | 5db2001a | Marios Kogias | if security_groups:
|
203 | 5db2001a | Marios Kogias | sg_list = [] |
204 | 5db2001a | Marios Kogias | #validate security groups
|
205 | 5db2001a | Marios Kogias | for gid in security_groups: |
206 | a1713485 | Marios Kogias | sg = util.get_security_group(int(gid))
|
207 | a1713485 | Marios Kogias | sg_list.append(sg) |
208 | 5db2001a | Marios Kogias | |
209 | 5db2001a | Marios Kogias | #clear the old security groups
|
210 | 5db2001a | Marios Kogias | port.security_groups.clear() |
211 | 5db2001a | Marios Kogias | |
212 | 5db2001a | Marios Kogias | #add the new groups
|
213 | 5db2001a | Marios Kogias | port.security_groups.add(*sg_list) |
214 | 5db2001a | Marios Kogias | |
215 | 5db2001a | Marios Kogias | port.save() |
216 | 5db2001a | Marios Kogias | return render_port(request, port_to_dict(port), 200) |
217 | 5db2001a | Marios Kogias | |
218 | 5db2001a | Marios Kogias | |
219 | 5db2001a | Marios Kogias | @api.api_method(http_method='DELETE', user_required=True, logger=log) |
220 | 5db2001a | Marios Kogias | @transaction.commit_on_success
|
221 | 5db2001a | Marios Kogias | def delete_port(request, port_id): |
222 | 5db2001a | Marios Kogias | log.info('delete_port %s', port_id)
|
223 | 86961519 | Christos Stavrakakis | user_id = request.user_uniq |
224 | 86961519 | Christos Stavrakakis | port = util.get_port(port_id, user_id, for_update=True)
|
225 | 6e73f499 | Christos Stavrakakis | |
226 | 6e73f499 | Christos Stavrakakis | # Deleting port that is connected to a public network is allowed only if
|
227 | 6e73f499 | Christos Stavrakakis | # the port has an associated floating IP address.
|
228 | 6e73f499 | Christos Stavrakakis | if port.network.public and not port.ips.filter(floating_ip=True, |
229 | 6e73f499 | Christos Stavrakakis | deleted=False).exists():
|
230 | 6e73f499 | Christos Stavrakakis | raise faults.Forbidden("Cannot disconnect from public network.") |
231 | 6e73f499 | Christos Stavrakakis | |
232 | fae6e5f0 | Christos Stavrakakis | servers.delete_port(port) |
233 | 5db2001a | Marios Kogias | return HttpResponse(status=204) |
234 | 5db2001a | Marios Kogias | |
235 | 593851e0 | Buildbot | #util functions
|
236 | 593851e0 | Buildbot | |
237 | 593851e0 | Buildbot | |
238 | 593851e0 | Buildbot | def port_to_dict(port, detail=True): |
239 | 593851e0 | Buildbot | d = {'id': str(port.id), 'name': port.name} |
240 | 24ea227b | Dionysis Grigoropoulos | d['links'] = util.port_to_links(port.id)
|
241 | 593851e0 | Buildbot | if detail:
|
242 | fae6e5f0 | Christos Stavrakakis | user_id = port.userid |
243 | fae6e5f0 | Christos Stavrakakis | machine_id = port.machine_id |
244 | 86961519 | Christos Stavrakakis | d['user_id'] = user_id
|
245 | 86961519 | Christos Stavrakakis | d['tenant_id'] = user_id
|
246 | fae6e5f0 | Christos Stavrakakis | d['device_id'] = str(machine_id) if machine_id else None |
247 | 86961519 | Christos Stavrakakis | # TODO: Change this based on the status of VM
|
248 | 593851e0 | Buildbot | d['admin_state_up'] = True |
249 | 593851e0 | Buildbot | d['mac_address'] = port.mac
|
250 | 593851e0 | Buildbot | d['status'] = port.state
|
251 | 593851e0 | Buildbot | d['device_owner'] = port.device_owner
|
252 | 883c1f94 | Christos Stavrakakis | d['network_id'] = str(port.network_id) |
253 | 5db2001a | Marios Kogias | d['updated'] = api.utils.isoformat(port.updated)
|
254 | 5db2001a | Marios Kogias | d['created'] = api.utils.isoformat(port.created)
|
255 | 593851e0 | Buildbot | d['fixed_ips'] = []
|
256 | 593851e0 | Buildbot | for ip in port.ips.all(): |
257 | 593851e0 | Buildbot | d['fixed_ips'].append({"ip_address": ip.address, |
258 | 883c1f94 | Christos Stavrakakis | "subnet": str(ip.subnet_id)}) |
259 | 883c1f94 | Christos Stavrakakis | # Avoid extra queries until security groups are implemented!
|
260 | 883c1f94 | Christos Stavrakakis | #sg_list = list(port.security_groups.values_list('id', flat=True))
|
261 | 883c1f94 | Christos Stavrakakis | d['security_groups'] = []
|
262 | 5db2001a | Marios Kogias | |
263 | 593851e0 | Buildbot | return d
|
264 | 593851e0 | Buildbot | |
265 | 593851e0 | Buildbot | |
266 | 593851e0 | Buildbot | def render_port(request, portdict, status=200): |
267 | 593851e0 | Buildbot | if request.serialization == 'xml': |
268 | 5db2001a | Marios Kogias | data = render_to_string('port.xml', {'port': portdict}) |
269 | 593851e0 | Buildbot | else:
|
270 | 593851e0 | Buildbot | data = json.dumps({'port': portdict})
|
271 | 593851e0 | Buildbot | return HttpResponse(data, status=status) |