Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / ports.py @ 2a58c29b

History | View | Annotate | Download (9.4 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 5db2001a Marios Kogias
    port_dicts = [port_to_dict(port, detail)
89 9f6760ee Christos Stavrakakis
                  for port in user_ports.order_by('id')]
90 593851e0 Buildbot
91 593851e0 Buildbot
    if request.serialization == 'xml':
92 5db2001a Marios Kogias
        data = render_to_string('list_ports.xml', {
93 5db2001a Marios Kogias
            "ports": port_dicts})
94 593851e0 Buildbot
    else:
95 5db2001a Marios Kogias
        data = json.dumps({'ports': port_dicts})
96 593851e0 Buildbot
97 593851e0 Buildbot
    return HttpResponse(data, status=200)
98 593851e0 Buildbot
99 593851e0 Buildbot
100 593851e0 Buildbot
@api.api_method(http_method='POST', user_required=True, logger=log)
101 fae6e5f0 Christos Stavrakakis
@transaction.commit_on_success
102 593851e0 Buildbot
def create_port(request):
103 593851e0 Buildbot
    user_id = request.user_uniq
104 5db2001a Marios Kogias
    req = api.utils.get_request_dict(request)
105 593851e0 Buildbot
    log.info('create_port %s', req)
106 593851e0 Buildbot
107 f16aa9e6 Marios Kogias
    port_dict = api.utils.get_attribute(req, "port")
108 f16aa9e6 Marios Kogias
    net_id = api.utils.get_attribute(port_dict, "network_id")
109 f16aa9e6 Marios Kogias
110 9f6760ee Christos Stavrakakis
    network = util.get_network(net_id, user_id, non_deleted=True)
111 f16aa9e6 Marios Kogias
112 69c8d65d Christos Stavrakakis
    # Check if the request contains a valid IPv4 address
113 69c8d65d Christos Stavrakakis
    fixed_ips = api.utils.get_attribute(port_dict, "fixed_ips", required=False)
114 69c8d65d Christos Stavrakakis
    if fixed_ips is not None and len(fixed_ips) > 0:
115 69c8d65d Christos Stavrakakis
        if len(fixed_ips) > 1:
116 69c8d65d Christos Stavrakakis
            msg = "'fixed_ips' attribute must contain only one fixed IP."
117 69c8d65d Christos Stavrakakis
            raise faults.BadRequest(msg)
118 69c8d65d Christos Stavrakakis
        fixed_ip_address = fixed_ips[0].get("ip_address")
119 69c8d65d Christos Stavrakakis
        if fixed_ip_address is not None:
120 69c8d65d Christos Stavrakakis
            try:
121 69c8d65d Christos Stavrakakis
                ip = ipaddr.IPAddress(fixed_ip_address)
122 69c8d65d Christos Stavrakakis
                if ip.version == 6:
123 69c8d65d Christos Stavrakakis
                    msg = "'ip_address' can be only an IPv4 address'"
124 69c8d65d Christos Stavrakakis
                    raise faults.BadRequest(msg)
125 69c8d65d Christos Stavrakakis
            except ValueError:
126 69c8d65d Christos Stavrakakis
                msg = "%s is not a valid IPv4 Address" % fixed_ip_address
127 69c8d65d Christos Stavrakakis
                raise faults.BadRequest(msg)
128 69c8d65d Christos Stavrakakis
    else:
129 69c8d65d Christos Stavrakakis
        fixed_ip_address = None
130 69c8d65d Christos Stavrakakis
131 9c74cc19 Marios Kogias
    ipaddress = None
132 f16aa9e6 Marios Kogias
    if network.public:
133 69c8d65d Christos Stavrakakis
        # Creating a port to a public network is only allowed if the user has
134 69c8d65d Christos Stavrakakis
        # already a floating IP address in this network which is specified
135 69c8d65d Christos Stavrakakis
        # as the fixed IP address of the port
136 69c8d65d Christos Stavrakakis
        if fixed_ip_address is None:
137 69c8d65d Christos Stavrakakis
            msg = ("'fixed_ips' attribute must contain a floating IP address"
138 69c8d65d Christos Stavrakakis
                   " in order to connect to a public network.")
139 69c8d65d Christos Stavrakakis
            raise faults.BadRequest(msg)
140 69c8d65d Christos Stavrakakis
        ipaddress = util.get_floating_ip_by_address(user_id, fixed_ip_address,
141 b968e8e6 Christos Stavrakakis
                                                    for_update=True)
142 69c8d65d Christos Stavrakakis
    elif fixed_ip_address:
143 0292883e Christos Stavrakakis
        ipaddress = ips.allocate_ip(network, user_id,
144 0292883e Christos Stavrakakis
                                    address=fixed_ip_address)
145 f16aa9e6 Marios Kogias
146 fae6e5f0 Christos Stavrakakis
    device_id = api.utils.get_attribute(port_dict, "device_id", required=False)
147 fae6e5f0 Christos Stavrakakis
    vm = None
148 fae6e5f0 Christos Stavrakakis
    if device_id is not None:
149 fae6e5f0 Christos Stavrakakis
        vm = util.get_vm(device_id, user_id, for_update=True, non_deleted=True,
150 fae6e5f0 Christos Stavrakakis
                         non_suspended=True)
151 f16aa9e6 Marios Kogias
152 f16aa9e6 Marios Kogias
    name = api.utils.get_attribute(port_dict, "name", required=False)
153 f16aa9e6 Marios Kogias
    if name is None:
154 f16aa9e6 Marios Kogias
        name = ""
155 f16aa9e6 Marios Kogias
156 f16aa9e6 Marios Kogias
    security_groups = api.utils.get_attribute(port_dict,
157 f16aa9e6 Marios Kogias
                                              "security_groups",
158 f16aa9e6 Marios Kogias
                                              required=False)
159 f16aa9e6 Marios Kogias
    #validate security groups
160 f16aa9e6 Marios Kogias
    # like get security group from db
161 a1713485 Marios Kogias
    sg_list = []
162 f16aa9e6 Marios Kogias
    if security_groups:
163 f16aa9e6 Marios Kogias
        for gid in security_groups:
164 a1713485 Marios Kogias
            sg = util.get_security_group(int(gid))
165 a1713485 Marios Kogias
            sg_list.append(sg)
166 f16aa9e6 Marios Kogias
167 fae6e5f0 Christos Stavrakakis
    new_port = servers.create_port(user_id, network, use_ipaddress=ipaddress,
168 fae6e5f0 Christos Stavrakakis
                                   machine=vm)
169 593851e0 Buildbot
170 5db2001a Marios Kogias
    response = render_port(request, port_to_dict(new_port), status=201)
171 593851e0 Buildbot
172 593851e0 Buildbot
    return response
173 593851e0 Buildbot
174 593851e0 Buildbot
175 5db2001a Marios Kogias
@api.api_method(http_method='GET', user_required=True, logger=log)
176 5db2001a Marios Kogias
def get_port_details(request, port_id):
177 5db2001a Marios Kogias
    log.debug('get_port_details %s', port_id)
178 5db2001a Marios Kogias
    port = util.get_port(port_id, request.user_uniq)
179 5db2001a Marios Kogias
    return render_port(request, port_to_dict(port))
180 5db2001a Marios Kogias
181 5db2001a Marios Kogias
182 5db2001a Marios Kogias
@api.api_method(http_method='PUT', user_required=True, logger=log)
183 5db2001a Marios Kogias
def update_port(request, port_id):
184 5db2001a Marios Kogias
    '''
185 5db2001a Marios Kogias
    You can update only name, security_groups
186 5db2001a Marios Kogias
    '''
187 5db2001a Marios Kogias
    port = util.get_port(port_id, request.user_uniq, for_update=True)
188 5db2001a Marios Kogias
    req = api.utils.get_request_dict(request)
189 5db2001a Marios Kogias
190 5db2001a Marios Kogias
    port_info = api.utils.get_attribute(req, "port", required=True)
191 5db2001a Marios Kogias
    name = api.utils.get_attribute(port_info, "name", required=False)
192 5db2001a Marios Kogias
193 5db2001a Marios Kogias
    if name:
194 5db2001a Marios Kogias
        port.name = name
195 5db2001a Marios Kogias
196 5db2001a Marios Kogias
    security_groups = api.utils.get_attribute(port_info, "security_groups",
197 5db2001a Marios Kogias
                                              required=False)
198 5db2001a Marios Kogias
    if security_groups:
199 5db2001a Marios Kogias
        sg_list = []
200 5db2001a Marios Kogias
        #validate security groups
201 5db2001a Marios Kogias
        for gid in security_groups:
202 a1713485 Marios Kogias
            sg = util.get_security_group(int(gid))
203 a1713485 Marios Kogias
            sg_list.append(sg)
204 5db2001a Marios Kogias
205 5db2001a Marios Kogias
        #clear the old security groups
206 5db2001a Marios Kogias
        port.security_groups.clear()
207 5db2001a Marios Kogias
208 5db2001a Marios Kogias
        #add the new groups
209 5db2001a Marios Kogias
        port.security_groups.add(*sg_list)
210 5db2001a Marios Kogias
211 5db2001a Marios Kogias
    port.save()
212 5db2001a Marios Kogias
    return render_port(request, port_to_dict(port), 200)
213 5db2001a Marios Kogias
214 5db2001a Marios Kogias
215 5db2001a Marios Kogias
@api.api_method(http_method='DELETE', user_required=True, logger=log)
216 5db2001a Marios Kogias
@transaction.commit_on_success
217 5db2001a Marios Kogias
def delete_port(request, port_id):
218 5db2001a Marios Kogias
    log.info('delete_port %s', port_id)
219 86961519 Christos Stavrakakis
    user_id = request.user_uniq
220 86961519 Christos Stavrakakis
    port = util.get_port(port_id, user_id, for_update=True)
221 6e73f499 Christos Stavrakakis
222 6e73f499 Christos Stavrakakis
    # Deleting port that is connected to a public network is allowed only if
223 6e73f499 Christos Stavrakakis
    # the port has an associated floating IP address.
224 6e73f499 Christos Stavrakakis
    if port.network.public and not port.ips.filter(floating_ip=True,
225 6e73f499 Christos Stavrakakis
                                                   deleted=False).exists():
226 6e73f499 Christos Stavrakakis
        raise faults.Forbidden("Cannot disconnect from public network.")
227 6e73f499 Christos Stavrakakis
228 fae6e5f0 Christos Stavrakakis
    servers.delete_port(port)
229 5db2001a Marios Kogias
    return HttpResponse(status=204)
230 5db2001a Marios Kogias
231 593851e0 Buildbot
#util functions
232 593851e0 Buildbot
233 593851e0 Buildbot
234 593851e0 Buildbot
def port_to_dict(port, detail=True):
235 593851e0 Buildbot
    d = {'id': str(port.id), 'name': port.name}
236 24ea227b Dionysis Grigoropoulos
    d['links'] = util.port_to_links(port.id)
237 593851e0 Buildbot
    if detail:
238 fae6e5f0 Christos Stavrakakis
        user_id = port.userid
239 fae6e5f0 Christos Stavrakakis
        machine_id = port.machine_id
240 86961519 Christos Stavrakakis
        d['user_id'] = user_id
241 86961519 Christos Stavrakakis
        d['tenant_id'] = user_id
242 fae6e5f0 Christos Stavrakakis
        d['device_id'] = str(machine_id) if machine_id else None
243 86961519 Christos Stavrakakis
        # TODO: Change this based on the status of VM
244 593851e0 Buildbot
        d['admin_state_up'] = True
245 593851e0 Buildbot
        d['mac_address'] = port.mac
246 593851e0 Buildbot
        d['status'] = port.state
247 593851e0 Buildbot
        d['device_owner'] = port.device_owner
248 593851e0 Buildbot
        d['network_id'] = str(port.network.id)
249 5db2001a Marios Kogias
        d['updated'] = api.utils.isoformat(port.updated)
250 5db2001a Marios Kogias
        d['created'] = api.utils.isoformat(port.created)
251 593851e0 Buildbot
        d['fixed_ips'] = []
252 593851e0 Buildbot
        for ip in port.ips.all():
253 593851e0 Buildbot
            d['fixed_ips'].append({"ip_address": ip.address,
254 9f6760ee Christos Stavrakakis
                                   "subnet": str(ip.subnet.id)})
255 5db2001a Marios Kogias
        sg_list = list(port.security_groups.values_list('id', flat=True))
256 5db2001a Marios Kogias
        d['security_groups'] = map(str, sg_list)
257 5db2001a Marios Kogias
258 593851e0 Buildbot
    return d
259 593851e0 Buildbot
260 593851e0 Buildbot
261 593851e0 Buildbot
def render_port(request, portdict, status=200):
262 593851e0 Buildbot
    if request.serialization == 'xml':
263 5db2001a Marios Kogias
        data = render_to_string('port.xml', {'port': portdict})
264 593851e0 Buildbot
    else:
265 593851e0 Buildbot
        data = json.dumps({'port': portdict})
266 593851e0 Buildbot
    return HttpResponse(data, status=status)