floating_ips.py 9.27 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
# Copyright 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

from django.conf.urls.defaults import patterns
from django.db import transaction
from django.http import HttpResponse
from django.utils import simplejson as json

from snf_django.lib import api
from snf_django.lib.api import faults, utils
from synnefo.api import util
Christos Stavrakakis's avatar
Christos Stavrakakis committed
42
from synnefo.logic import ips
43
from synnefo.db.models import Network, IPAddress
44 45 46 47

from logging import getLogger
log = getLogger(__name__)

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

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

ips_urlpatterns = patterns(
    'synnefo.api.floating_ips',
    (r'^(?:/|.json|.xml)?$', 'demux'),
    (r'^/detail(?:.json|.xml)?$', 'list_floating_ips', {'detail': True}),
    (r'^/(\w+)(?:/|.json|.xml)?$', 'floating_ip_demux'))
66

67 68 69 70 71 72 73

def demux(request):
    if request.method == 'GET':
        return list_floating_ips(request)
    elif request.method == 'POST':
        return allocate_floating_ip(request)
    else:
74 75
        return api.api_method_not_allowed(request,
                                          allowed_methods=['GET', 'POST'])
76 77 78 79 80 81 82


def floating_ip_demux(request, floating_ip_id):
    if request.method == 'GET':
        return get_floating_ip(request, floating_ip_id)
    elif request.method == 'DELETE':
        return release_floating_ip(request, floating_ip_id)
83 84
    elif request.method == 'PUT':
        return update_floating_ip(request, floating_ip_id)
85
    else:
86 87
        return api.api_method_not_allowed(request,
                                          allowed_methods=['GET', 'DELETE'])
88 89 90


def ip_to_dict(floating_ip):
91
    machine_id = None
92
    port_id = None
93 94
    if floating_ip.nic is not None:
        machine_id = floating_ip.nic.machine_id
95 96
        port_id = floating_ip.nic.id
    return {"fixed_ip_address": None,
97 98
            "id": str(floating_ip.id),
            "instance_id": str(machine_id) if machine_id else None,
99
            "floating_ip_address": floating_ip.address,
100
            "port_id": str(port_id) if port_id else None,
101 102
            "floating_network_id": str(floating_ip.network_id),
            "deleted": floating_ip.deleted}
103 104


105 106
@api.api_method(http_method="GET", user_required=True, logger=log,
                serializations=["json"])
107 108 109 110 111
def list_floating_ips(request):
    """Return user reserved floating IPs"""
    log.debug("list_floating_ips")

    userid = request.user_uniq
112 113 114
    floating_ips = IPAddress.objects.filter(userid=userid, deleted=False,
                                            floating_ip=True).order_by("id")\
                                    .select_related("nic")
115
    floating_ips = utils.filter_modified_since(request, objects=floating_ips)
116 117 118 119

    floating_ips = map(ip_to_dict, floating_ips)

    request.serialization = "json"
120
    data = json.dumps({"floatingips": floating_ips})
121 122 123 124

    return HttpResponse(data, status=200)


125 126
@api.api_method(http_method="GET", user_required=True, logger=log,
                serializations=["json"])
127 128 129
def get_floating_ip(request, floating_ip_id):
    """Return information for a floating IP."""
    userid = request.user_uniq
130
    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id)
131
    request.serialization = "json"
132
    data = json.dumps({"floatingip": ip_to_dict(floating_ip)})
133 134 135
    return HttpResponse(data, status=200)


136 137
@api.api_method(http_method='POST', user_required=True, logger=log,
                serializations=["json"])
138
@transaction.commit_on_success
139 140 141
def allocate_floating_ip(request):
    """Allocate a floating IP."""
    req = utils.get_request_dict(request)
142
    floating_ip_dict = api.utils.get_attribute(req, "floatingip",
143
                                               required=True, attr_type=dict)
144 145 146
    log.info('allocate_floating_ip %s', req)

    userid = request.user_uniq
147
    project = floating_ip_dict.get("project", None)
148 149

    # the network_pool is a mandatory field
150 151
    network_id = api.utils.get_attribute(floating_ip_dict,
                                         "floating_network_id",
152 153
                                         required=False,
                                         attr_type=(basestring, int))
154
    if network_id is None:
155
        floating_ip = ips.create_floating_ip(userid, project=project)
156 157 158 159 160 161 162 163 164 165
    else:
        try:
            network_id = int(network_id)
        except ValueError:
            raise faults.BadRequest("Invalid networkd ID.")

        network = util.get_network(network_id, userid, for_update=True,
                                   non_deleted=True)
        address = api.utils.get_attribute(floating_ip_dict,
                                          "floating_ip_address",
166 167
                                          required=False,
                                          attr_type=basestring)
168 169
        floating_ip = ips.create_floating_ip(userid, network, address,
                                             project=project)
170 171

    log.info("User '%s' allocated floating IP '%s'", userid, floating_ip)
172
    request.serialization = "json"
173
    data = json.dumps({"floatingip": ip_to_dict(floating_ip)})
174 175 176
    return HttpResponse(data, status=200)


177 178
@api.api_method(http_method='DELETE', user_required=True, logger=log,
                serializations=["json"])
179 180 181 182 183
@transaction.commit_on_success
def release_floating_ip(request, floating_ip_id):
    """Release a floating IP."""
    userid = request.user_uniq
    log.info("release_floating_ip '%s'. User '%s'.", floating_ip_id, userid)
184 185 186

    floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
                                             for_update=True)
Christos Stavrakakis's avatar
Christos Stavrakakis committed
187
    ips.delete_floating_ip(floating_ip)
188 189 190
    log.info("User '%s' released IP '%s", userid, floating_ip)

    return HttpResponse(status=204)
191 192


193 194 195 196 197
@api.api_method(http_method='PUT', user_required=True, logger=log,
                serializations=["json"])
@transaction.commit_on_success
def update_floating_ip(request, floating_ip_id):
    """Update a floating IP."""
Christos Stavrakakis's avatar
Christos Stavrakakis committed
198
    raise faults.NotImplemented("Updating a floating IP is not supported.")
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    #userid = request.user_uniq
    #log.info("update_floating_ip '%s'. User '%s'.", floating_ip_id, userid)

    #req = utils.get_request_dict(request)
    #info = api.utils.get_attribute(req, "floatingip", required=True)

    #device_id = api.utils.get_attribute(info, "device_id", required=False)

    #floating_ip = util.get_floating_ip_by_id(userid, floating_ip_id,
    #                                         for_update=True)
    #if device_id:
    #    # attach
    #    vm = util.get_vm(device_id, userid)
    #    nic, floating_ip = servers.create_nic(vm, ipaddress=floating_ip)
    #    backend.connect_to_network(vm, nic)
    #else:
    #    # dettach
    #    nic = floating_ip.nic
    #    if not nic:
    #        raise faults.BadRequest("The floating IP is not associated\
    #                                with any device")
    #    vm = nic.machine
    #    servers.disconnect(vm, nic)
    #return HttpResponse(status=202)
223 224


225
# Floating IP pools
226 227
@api.api_method(http_method='GET', user_required=True, logger=log,
                serializations=["json"])
228
def list_floating_ip_pools(request):
229 230
    networks = Network.objects.filter(public=True, floating_ip_pool=True,
                                      deleted=False)
231
    networks = utils.filter_modified_since(request, objects=networks)
232
    floating_ip_pools = map(network_to_floating_ip_pool, networks)
233
    request.serialization = "json"
234
    data = json.dumps({"floating_ip_pools": floating_ip_pools})
235 236
    request.serialization = "json"
    return HttpResponse(data, status=200)
237 238 239 240 241 242 243


def network_to_floating_ip_pool(network):
    """Convert a 'Network' object to a floating IP pool dict."""
    total, free = network.ip_count()
    return {"name": str(network.id),
            "size": total,
244 245
            "free": free,
            "deleted": network.deleted}