Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / ports.py @ 435bb7fb

History | View | Annotate | Download (9.1 kB)

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.conf import settings
35
import ipaddr
36
from django.conf.urls import patterns
37
from django.http import HttpResponse
38
from django.utils import simplejson as json
39
from django.db import transaction
40
from django.template.loader import render_to_string
41

    
42
from snf_django.lib import api
43
from snf_django.lib.api import faults
44

    
45
from synnefo.api import util
46
from synnefo.db.models import NetworkInterface
47
from synnefo.logic import servers, ips
48

    
49
from logging import getLogger
50

    
51
log = getLogger(__name__)
52

    
53
urlpatterns = patterns(
54
    'synnefo.api.ports',
55
    (r'^(?:/|.json|.xml)?$', 'demux'),
56
    (r'^/detail(?:.json|.xml)?$', 'list_ports', {'detail': True}),
57
    (r'^/([-\w]+)(?:/|.json|.xml)?$', 'port_demux'))
58

    
59

    
60
def demux(request):
61
    if request.method == 'GET':
62
        return list_ports(request)
63
    elif request.method == 'POST':
64
        return create_port(request)
65
    else:
66
        return api.api_method_not_allowed(request)
67

    
68

    
69
def port_demux(request, port_id):
70

    
71
    if request.method == 'GET':
72
        return get_port_details(request, port_id)
73
    elif request.method == 'DELETE':
74
        return delete_port(request, port_id)
75
    elif request.method == 'PUT':
76
        return update_port(request, port_id)
77
    else:
78
        return api.api_method_not_allowed(request)
79

    
80

    
81
@api.api_method(http_method='GET', user_required=True, logger=log)
82
def list_ports(request, detail=False):
83

    
84
    log.debug('list_ports detail=%s', detail)
85

    
86
    user_ports = NetworkInterface.objects.filter(userid=request.user_uniq)
87

    
88
    port_dicts = [port_to_dict(port, detail)
89
                  for port in user_ports.order_by('id')]
90

    
91
    if request.serialization == 'xml':
92
        data = render_to_string('list_ports.xml', {
93
            "ports": port_dicts})
94
    else:
95
        data = json.dumps({'ports': port_dicts})
96

    
97
    return HttpResponse(data, status=200)
98

    
99

    
100
@api.api_method(http_method='POST', user_required=True, logger=log)
101
@transaction.commit_on_success
102
def create_port(request):
103
    user_id = request.user_uniq
104
    req = api.utils.get_request_dict(request)
105
    log.info('create_port %s', req)
106

    
107
    port_dict = api.utils.get_attribute(req, "port")
108
    net_id = api.utils.get_attribute(port_dict, "network_id")
109

    
110
    network = util.get_network(net_id, user_id, non_deleted=True)
111

    
112
    # Check if the request contains a valid IPv4 address
113
    fixed_ips = api.utils.get_attribute(port_dict, "fixed_ips", required=False)
114
    if fixed_ips is not None and len(fixed_ips) > 0:
115
        if len(fixed_ips) > 1:
116
            msg = "'fixed_ips' attribute must contain only one fixed IP."
117
            raise faults.BadRequest(msg)
118
        fixed_ip_address = fixed_ips[0].get("ip_address")
119
        if fixed_ip_address is not None:
120
            try:
121
                ip = ipaddr.IPAddress(fixed_ip_address)
122
                if ip.version == 6:
123
                    msg = "'ip_address' can be only an IPv4 address'"
124
                    raise faults.BadRequest(msg)
125
            except ValueError:
126
                msg = "%s is not a valid IPv4 Address" % fixed_ip_address
127
                raise faults.BadRequest(msg)
128
    else:
129
        fixed_ip_address = None
130

    
131
    ipaddress = None
132
    if network.public:
133
        # Creating a port to a public network is only allowed if the user has
134
        # already a floating IP address in this network which is specified
135
        # as the fixed IP address of the port
136
        if fixed_ip_address is None:
137
            msg = ("'fixed_ips' attribute must contain a floating IP address"
138
                   " in order to connect to a public network.")
139
            raise faults.BadRequest(msg)
140
        ipaddress = util.get_floating_ip_by_address(user_id, fixed_ip_address,
141
                                                    for_update=True)
142
    elif fixed_ip_address:
143
        ipaddress = ips.allocate_ip(network, user_id,
144
                                    address=fixed_ip_address)
145

    
146
    device_id = api.utils.get_attribute(port_dict, "device_id", required=False)
147
    vm = None
148
    if device_id is not None:
149
        vm = util.get_vm(device_id, user_id, for_update=True, non_deleted=True,
150
                         non_suspended=True)
151

    
152
    name = api.utils.get_attribute(port_dict, "name", required=False)
153
    if name is None:
154
        name = ""
155

    
156
    security_groups = api.utils.get_attribute(port_dict,
157
                                              "security_groups",
158
                                              required=False)
159
    #validate security groups
160
    # like get security group from db
161
    sg_list = []
162
    if security_groups:
163
        for gid in security_groups:
164
            sg = util.get_security_group(int(gid))
165
            sg_list.append(sg)
166

    
167
    new_port = servers.create_port(user_id, network, use_ipaddress=ipaddress,
168
                                   machine=vm)
169

    
170
    response = render_port(request, port_to_dict(new_port), status=201)
171

    
172
    return response
173

    
174

    
175
@api.api_method(http_method='GET', user_required=True, logger=log)
176
def get_port_details(request, port_id):
177
    log.debug('get_port_details %s', port_id)
178
    port = util.get_port(port_id, request.user_uniq)
179
    return render_port(request, port_to_dict(port))
180

    
181

    
182
@api.api_method(http_method='PUT', user_required=True, logger=log)
183
def update_port(request, port_id):
184
    '''
185
    You can update only name, security_groups
186
    '''
187
    port = util.get_port(port_id, request.user_uniq, for_update=True)
188
    req = api.utils.get_request_dict(request)
189

    
190
    port_info = api.utils.get_attribute(req, "port", required=True)
191
    name = api.utils.get_attribute(port_info, "name", required=False)
192

    
193
    if name:
194
        port.name = name
195

    
196
    security_groups = api.utils.get_attribute(port_info, "security_groups",
197
                                              required=False)
198
    if security_groups:
199
        sg_list = []
200
        #validate security groups
201
        for gid in security_groups:
202
            sg = util.get_security_group(int(gid))
203
            sg_list.append(sg)
204

    
205
        #clear the old security groups
206
        port.security_groups.clear()
207

    
208
        #add the new groups
209
        port.security_groups.add(*sg_list)
210

    
211
    port.save()
212
    return render_port(request, port_to_dict(port), 200)
213

    
214

    
215
@api.api_method(http_method='DELETE', user_required=True, logger=log)
216
@transaction.commit_on_success
217
def delete_port(request, port_id):
218
    log.info('delete_port %s', port_id)
219
    user_id = request.user_uniq
220
    port = util.get_port(port_id, user_id, for_update=True)
221
    servers.delete_port(port)
222
    return HttpResponse(status=204)
223

    
224
#util functions
225

    
226

    
227
def port_to_dict(port, detail=True):
228
    d = {'id': str(port.id), 'name': port.name}
229
    d['links'] = util.port_to_links(port.id)
230
    if detail:
231
        user_id = port.userid
232
        machine_id = port.machine_id
233
        d['user_id'] = user_id
234
        d['tenant_id'] = user_id
235
        d['device_id'] = str(machine_id) if machine_id else None
236
        # TODO: Change this based on the status of VM
237
        d['admin_state_up'] = True
238
        d['mac_address'] = port.mac
239
        d['status'] = port.state
240
        d['device_owner'] = port.device_owner
241
        d['network_id'] = str(port.network.id)
242
        d['updated'] = api.utils.isoformat(port.updated)
243
        d['created'] = api.utils.isoformat(port.created)
244
        d['fixed_ips'] = []
245
        for ip in port.ips.all():
246
            d['fixed_ips'].append({"ip_address": ip.address,
247
                                   "subnet": str(ip.subnet.id)})
248
        sg_list = list(port.security_groups.values_list('id', flat=True))
249
        d['security_groups'] = map(str, sg_list)
250

    
251
    return d
252

    
253

    
254
def render_port(request, portdict, status=200):
255
    if request.serialization == 'xml':
256
        data = render_to_string('port.xml', {'port': portdict})
257
    else:
258
        data = json.dumps({'port': portdict})
259
    return HttpResponse(data, status=status)