Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / ports.py @ 883c1f94

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 593851e0 Buildbot
def list_ports(request, detail=False):
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)