synnefo.py 4.93 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
# Copyright 2016 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, self.list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, self.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.

import json
29
from kamaki.clients import ClientError
30
from kamaki.clients.astakos import AstakosClient
31 32
from kamaki.clients.cyclades import (
    CycladesComputeClient, CycladesNetworkClient)
33
from kamaki.clients.utils import https
34
from soi.config import AUTH_URL, CA_CERTS
35
import webob.exc
36

37 38
#  endpoints are offered auth-free, so no need for an admin token
ADMIN_TOKEN = ''
39
https.patch_with_certs(CA_CERTS)
40
auth = AstakosClient(AUTH_URL, ADMIN_TOKEN)
41

42 43 44 45 46
endpoints = {'identity': AUTH_URL}
client_classes = {'identity': AstakosClient}

for cls in (CycladesComputeClient, CycladesNetworkClient):
    service_type = cls.service_type
47 48 49 50
    endpoints[service_type] = auth.get_endpoint_url(service_type)
    client_classes[service_type] = cls


51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
def handle_exceptions(f):
    """Run a method, raise Synnefo errors as snf-occi exceptions"""
    def wrapper(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except ClientError as ce:
            print 'ClientError:', ce, ce.status, ce.details
            exc = {
                400: webob.exc.HTTPBadRequest,
                401: webob.exc.HTTPUnauthorized,
                403: webob.exc.HTTPForbidden,
                404: webob.exc.HTTPNotFound,
                405: webob.exc.HTTPMethodNotAllowed,
                406: webob.exc.HTTPNotAcceptable,
                409: webob.exc.HTTPConflict,
                413: webob.exc.HTTPRequestEntityTooLarge,
                415: webob.exc.HTTPUnsupportedMediaType,
                429: webob.exc.HTTPTooManyRequests,
                501: webob.exc.HTTPNotImplemented,
                503: webob.exc.HTTPServiceUnavailable,
            }.get(ce.status, webob.exc.HTTPInternalServerError)
            raise exc(explanation='{0}'.format(ce.message))
    wrapper.__name__ = f.__name__
    return wrapper


@handle_exceptions
78 79 80 81 82 83 84 85 86 87 88
def call_kamaki(environ, start_response, *args, **kwargs):
    """Initialize the requested kamaki client, call the requested method
    :param cls: the kamaki client Class, e.g, CycladesComputeClient
    :param method_name: name of the method to call, e.g. list_servers
    :param args: args for the method method
    :param kwargs: kwargs for the method
    :returns: the response from kamaki, WSGI compliant
    """
    service_type = environ.pop('service_type')
    method_name = environ.pop('method_name')
    kwargs = environ.pop('kwargs', {})
89 90
    print '\t', service_type, method_name, kwargs

91 92 93 94
    if service_type in ('mock', ):
        code, status, headers, body = {
            'empty_list': (200, 'OK', {}, {'empty list': []}),
        }.get(method_name, kwargs.get('req_args', (200, 'OK', {}, None)))
95

96 97 98 99 100 101
    else:  # Normal case
        endpoint = endpoints[service_type]
        token = environ['HTTP_X_AUTH_TOKEN']
        cls = client_classes[service_type]
        client = cls(endpoint, token)
        method = getattr(client, method_name)
102

103 104
        r = method(*args, **kwargs)
        code, status, headers = r.status_code, r.status, r.headers
105 106 107 108
        try:
            body = _stringify_json_values(r.json)
        except ClientError:
            body = None
109

110
    bodystr = ''
111 112 113 114
    if body is not None:
        bodystr = json.dumps(body)
        headers['content-length'] = '{0}'.format(len(bodystr))
        headers.setdefault('content-type', 'application/json')
115

116
    start_response('{0} {1}'.format(code, status), headers.items())
117 118 119 120 121 122
    return bodystr


def _stringify_json_values(data):
    """If a sinlge value is not a string, make it"""
    if isinstance(data, dict):
123
        return dict((k, _stringify_json_values(v)) for k, v in data.items())
124 125 126
    if isinstance(data, list):
        return map(_stringify_json_values, data)
    return '{0}'.format(data) if data else data