Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / ports.py @ 3f18f035

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