Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / floating_ips.py @ 9d1e6480

History | View | Annotate | Download (7.8 kB)

1
# Copyright 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.urls.defaults import patterns
35
from django.db import transaction
36
from django.http import HttpResponse
37
from django.utils import simplejson as json
38

    
39
from snf_django.lib import api
40
from snf_django.lib.api import faults, utils
41
from synnefo.api import util
42
from synnefo import quotas
43
from synnefo.db.models import Network, IPAddress
44
from synnefo.db import pools
45

    
46

    
47
from logging import getLogger
48
log = getLogger(__name__)
49

    
50
ips_urlpatterns = patterns(
51
    'synnefo.api.floating_ips',
52
    (r'^(?:/|.json|.xml)?$', 'demux'),
53
    (r'^/(\w+)(?:.json|.xml)?$', 'floating_ip_demux'),
54
)
55

    
56
pools_urlpatterns = patterns(
57
    "synnefo.api.floating_ips",
58
    (r'^(?:/|.json|.xml)?$', 'list_floating_ip_pools'),
59
)
60

    
61

    
62
def demux(request):
63
    if request.method == 'GET':
64
        return list_floating_ips(request)
65
    elif request.method == 'POST':
66
        return allocate_floating_ip(request)
67
    else:
68
        return api.api_method_not_allowed(request)
69

    
70

    
71
def floating_ip_demux(request, floating_ip_id):
72
    if request.method == 'GET':
73
        return get_floating_ip(request, floating_ip_id)
74
    elif request.method == 'DELETE':
75
        return release_floating_ip(request, floating_ip_id)
76
    else:
77
        return api.api_method_not_allowed(request)
78

    
79

    
80
def ip_to_dict(floating_ip):
81
    machine_id = None
82
    if floating_ip.nic is not None:
83
        machine_id = floating_ip.nic.machine_id
84
    return {"fixed_ip": None,
85
            "id": str(floating_ip.id),
86
            "instance_id": str(machine_id) if machine_id else None,
87
            "ip": floating_ip.address,
88
            "pool": str(floating_ip.network_id)}
89

    
90

    
91
@api.api_method(http_method="GET", user_required=True, logger=log,
92
                serializations=["json"])
93
def list_floating_ips(request):
94
    """Return user reserved floating IPs"""
95
    log.debug("list_floating_ips")
96

    
97
    userid = request.user_uniq
98
    floating_ips = IPAddress.objects.filter(userid=userid, deleted=False,
99
                                            floating_ip=True).order_by("id")\
100
                                    .select_related("nic")
101
    floating_ips = utils.filter_modified_since(request, objects=floating_ips)
102

    
103
    floating_ips = map(ip_to_dict, floating_ips)
104

    
105
    request.serialization = "json"
106
    data = json.dumps({"floating_ips": floating_ips})
107

    
108
    return HttpResponse(data, status=200)
109

    
110

    
111
@api.api_method(http_method="GET", user_required=True, logger=log,
112
                serializations=["json"])
113
def get_floating_ip(request, floating_ip_id):
114
    """Return information for a floating IP."""
115
    userid = request.user_uniq
116
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id)
117
    request.serialization = "json"
118
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
119
    return HttpResponse(data, status=200)
120

    
121

    
122
@api.api_method(http_method='POST', user_required=True, logger=log,
123
                serializations=["json"])
124
@transaction.commit_on_success
125
def allocate_floating_ip(request):
126
    """Allocate a floating IP."""
127
    req = utils.get_request_dict(request)
128
    log.info('allocate_floating_ip %s', req)
129

    
130
    userid = request.user_uniq
131
    pool = req.get("pool", None)
132
    address = req.get("address", None)
133

    
134
    if pool is None:
135
        # User did not specified a pool. Choose a random public IP
136
        try:
137
            floating_ip = util.allocate_public_ip(userid=userid,
138
                                                  floating_ip=True)
139
        except pools.EmptyPool:
140
            raise faults.Conflict("No more IP addresses available.")
141
    else:
142
        try:
143
            network_id = int(pool)
144
        except ValueError:
145
            raise faults.BadRequest("Invalid pool ID.")
146
        network = util.get_network(network_id, userid, for_update=True,
147
                                   non_deleted=True)
148
        if not network.floating_ip_pool:
149
            # Check that it is a floating IP pool
150
            raise faults.ItemNotFound("Floating IP pool %s does not exist." %
151
                                      network_id)
152
        floating_ip = util.allocate_ip(network, userid, address=address,
153
                                       floating_ip=True)
154

    
155
    quotas.issue_and_accept_commission(floating_ip)
156
    transaction.commit()
157

    
158
    log.info("User '%s' allocated floating IP '%s'", userid, floating_ip)
159

    
160
    request.serialization = "json"
161
    data = json.dumps({"floating_ip": ip_to_dict(floating_ip)})
162
    return HttpResponse(data, status=200)
163

    
164

    
165
@api.api_method(http_method='DELETE', user_required=True, logger=log,
166
                serializations=["json"])
167
@transaction.commit_on_success
168
def release_floating_ip(request, floating_ip_id):
169
    """Release a floating IP."""
170
    userid = request.user_uniq
171
    log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
172

    
173
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
174
                                             for_update=True)
175
    if floating_ip.nic:
176
        # This is safe, you also need for_update to attach floating IP to
177
        # instance.
178
        msg = "Floating IP '%s' is attached to instance." % floating_ip.id
179
        raise faults.Conflict(msg)
180

    
181
    # Return the address of the floating IP back to pool
182
    floating_ip.release_address()
183
    # And mark the floating IP as deleted
184
    floating_ip.deleted = True
185
    floating_ip.save()
186
    # Release quota for floating IP
187
    quotas.issue_and_accept_commission(floating_ip, delete=True)
188
    transaction.commit()
189
    # Delete the floating IP from DB
190
    floating_ip.delete()
191

    
192
    log.info("User '%s' released IP '%s", userid, floating_ip)
193

    
194
    return HttpResponse(status=204)
195

    
196

    
197
# Floating IP pools
198
@api.api_method(http_method='GET', user_required=True, logger=log,
199
                serializations=["json"])
200
def list_floating_ip_pools(request):
201
    networks = Network.objects.filter(public=True, floating_ip_pool=True,
202
                                      deleted=False)
203
    networks = utils.filter_modified_since(request, objects=networks)
204
    pools = map(network_to_floating_ip_pool, networks)
205
    request.serialization = "json"
206
    data = json.dumps({"floating_ip_pools": pools})
207
    request.serialization = "json"
208
    return HttpResponse(data, status=200)
209

    
210

    
211
def network_to_floating_ip_pool(network):
212
    """Convert a 'Network' object to a floating IP pool dict."""
213
    total, free = network.ip_count()
214
    return {"name": str(network.id),
215
            "size": total,
216
            "free": free}