Split pithos components in separate packages
[pithos] / pithos / api / functions.py
diff --git a/pithos/api/functions.py b/pithos/api/functions.py
deleted file mode 100644 (file)
index ff3e1c9..0000000
+++ /dev/null
@@ -1,552 +0,0 @@
-#\r
-# Copyright (c) 2011 Greek Research and Technology Network\r
-#\r
-\r
-from django.http import HttpResponse\r
-from django.template.loader import render_to_string\r
-from django.utils import simplejson as json\r
-from django.utils.http import http_date, parse_etags\r
-\r
-try:\r
-    from django.utils.http import parse_http_date_safe\r
-except:\r
-    from pithos.api.util import parse_http_date_safe\r
-\r
-from pithos.api.faults import Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, Conflict, LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity\r
-from pithos.api.util import get_meta, get_range, api_method\r
-\r
-from settings import PROJECT_PATH\r
-from os import path\r
-STORAGE_PATH = path.join(PROJECT_PATH, 'data')\r
-\r
-from pithos.backends.dummy import BackEnd\r
-\r
-import logging\r
-\r
-logging.basicConfig(level=logging.INFO)\r
-\r
-@api_method('GET')\r
-def authenticate(request):\r
-    # Normal Response Codes: 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    x_auth_user = request.META.get('HTTP_X_AUTH_USER')\r
-    x_auth_key = request.META.get('HTTP_X_AUTH_KEY')\r
-    \r
-    if not x_auth_user or not x_auth_key:\r
-        raise BadRequest('Missing auth user or key.')\r
-    \r
-    response = HttpResponse(status = 204)\r
-    response['X-Auth-Token'] = 'eaaafd18-0fed-4b3a-81b4-663c99ec1cbb'\r
-    # TODO: Must support X-Storage-Url to be compatible.\r
-    response['X-Storage-Url'] = 'http://127.0.0.1:8000/v1/asdf'\r
-    return response\r
-\r
-def account_demux(request, v_account):\r
-    if request.method == 'HEAD':\r
-        return account_meta(request, v_account)\r
-    elif request.method == 'GET':\r
-        return container_list(request, v_account)\r
-    elif request.method == 'POST':\r
-        return account_update(request, v_account)\r
-    else:\r
-        return method_not_allowed(request)\r
-\r
-def container_demux(request, v_account, v_container):\r
-    if request.method == 'HEAD':\r
-        return container_meta(request, v_account, v_container)\r
-    elif request.method == 'GET':\r
-        return object_list(request, v_account, v_container)\r
-    elif request.method == 'PUT':\r
-        return container_create(request, v_account, v_container)\r
-    elif request.method == 'POST':\r
-        return container_update(request, v_account, v_container)\r
-    elif request.method == 'DELETE':\r
-        return container_delete(request, v_account, v_container)\r
-    else:\r
-        return method_not_allowed(request)\r
-\r
-def object_demux(request, v_account, v_container, v_object):\r
-    if request.method == 'HEAD':\r
-        return object_meta(request, v_account, v_container, v_object)\r
-    elif request.method == 'GET':\r
-        return object_read(request, v_account, v_container, v_object)\r
-    elif request.method == 'PUT':\r
-        return object_write(request, v_account, v_container, v_object)\r
-    elif request.method == 'COPY':\r
-        return object_copy(request, v_account, v_container, v_object)\r
-    elif request.method == 'POST':\r
-        return object_update(request, v_account, v_container, v_object)\r
-    elif request.method == 'DELETE':\r
-        return object_delete(request, v_account, v_container, v_object)\r
-    else:\r
-        return method_not_allowed(request)\r
-\r
-@api_method('HEAD')\r
-def account_meta(request, v_account):\r
-    # Normal Response Codes: 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        info = be.get_account_meta(request.user)\r
-    except NameError:\r
-        info = {'count': 0, 'bytes': 0}\r
-    \r
-    response = HttpResponse(status = 204)\r
-    response['X-Account-Container-Count'] = info['count']\r
-    response['X-Account-Bytes-Used'] = info['bytes']\r
-    for k in [x for x in info.keys() if x.startswith('X-Account-Meta-')]:\r
-        response[k.encode('utf-8')] = info[k].encode('utf-8')\r
-    \r
-    return response\r
-\r
-@api_method('POST')\r
-def account_update(request, v_account):\r
-    # Normal Response Codes: 202\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    meta = get_meta(request, 'X-Account-Meta-')\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    be.update_account_meta(request.user, meta)\r
-    \r
-    return HttpResponse(status = 202)\r
-\r
-@api_method('GET', format_allowed = True)\r
-def container_list(request, v_account):\r
-    # Normal Response Codes: 200, 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    marker = request.GET.get('marker')\r
-    limit = request.GET.get('limit')\r
-    if limit:\r
-        try:\r
-            limit = int(limit)\r
-        except ValueError:\r
-            limit = 10000\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        containers = be.list_containers(request.user, marker, limit)\r
-    except NameError:\r
-        containers = []\r
-    # TODO: The cloudfiles python bindings expect 200 if json/xml.\r
-    if len(containers) == 0:\r
-        return HttpResponse(status = 204)\r
-    \r
-    if request.serialization == 'text':\r
-        return HttpResponse('\n'.join(containers), status = 200)\r
-    \r
-    # TODO: Do this with a backend parameter?\r
-    try:\r
-        containers = [be.get_container_meta(request.user, x) for x in containers]\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    # TODO: Format dates.\r
-    if request.serialization == 'xml':\r
-        data = render_to_string('containers.xml', {'account': request.user, 'containers': containers})\r
-    elif request.serialization  == 'json':\r
-        data = json.dumps(containers)\r
-    return HttpResponse(data, status = 200)\r
-\r
-@api_method('HEAD')\r
-def container_meta(request, v_account, v_container):\r
-    # Normal Response Codes: 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        info = be.get_container_meta(request.user, v_container)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    response = HttpResponse(status = 204)\r
-    response['X-Container-Object-Count'] = info['count']\r
-    response['X-Container-Bytes-Used'] = info['bytes']\r
-    for k in [x for x in info.keys() if x.startswith('X-Container-Meta-')]:\r
-        response[k.encode('utf-8')] = info[k].encode('utf-8')\r
-    \r
-    return response\r
-\r
-@api_method('PUT')\r
-def container_create(request, v_account, v_container):\r
-    # Normal Response Codes: 201, 202\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    meta = get_meta(request, 'X-Container-Meta-')\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        be.create_container(request.user, v_container)\r
-        ret = 201\r
-    except NameError:\r
-        ret = 202\r
-    \r
-    if len(meta) > 0:\r
-        be.update_container_meta(request.user, v_container, meta)\r
-    \r
-    return HttpResponse(status = ret)\r
-\r
-@api_method('POST')\r
-def container_update(request, v_account, v_container):\r
-    # Normal Response Codes: 202\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    meta = get_meta(request, 'X-Container-Meta-')\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        be.update_container_meta(request.user, v_container, meta)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    return HttpResponse(status = 202)\r
-\r
-@api_method('DELETE')\r
-def container_delete(request, v_account, v_container):\r
-    # Normal Response Codes: 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       conflict (409),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        info = be.get_container_meta(request.user, v_container)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    if info['count'] > 0:\r
-        raise Conflict()\r
-    \r
-    # TODO: Handle both exceptions.\r
-    try:\r
-        be.delete_container(request.user, v_container)\r
-    except:\r
-        raise ItemNotFound()\r
-    return HttpResponse(status = 204)\r
-\r
-@api_method('GET', format_allowed = True)\r
-def object_list(request, v_account, v_container):\r
-    # Normal Response Codes: 200, 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    path = request.GET.get('path')\r
-    prefix = request.GET.get('prefix')\r
-    delimiter = request.GET.get('delimiter')\r
-    \r
-    # Path overrides prefix and delimiter.\r
-    if path:\r
-        prefix = path\r
-        delimiter = '/'\r
-    # Naming policy.\r
-    if prefix and delimiter:\r
-        prefix = prefix + delimiter\r
-    if not prefix:\r
-        prefix = ''\r
-    \r
-    marker = request.GET.get('marker')\r
-    limit = request.GET.get('limit')\r
-    if limit:\r
-        try:\r
-            limit = int(limit)\r
-        except ValueError:\r
-            limit = 10000\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        objects = be.list_objects(request.user, v_container, prefix, delimiter, marker, limit)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    # TODO: The cloudfiles python bindings expect 200 if json/xml.\r
-    if len(objects) == 0:\r
-        return HttpResponse(status = 204)\r
-    \r
-    if request.serialization == 'text':\r
-        return HttpResponse('\n'.join(objects), status = 200)\r
-    \r
-    # TODO: Do this with a backend parameter?\r
-    try:\r
-        objects = [be.get_object_meta(request.user, v_container, x) for x in objects]\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    # TODO: Format dates.\r
-    if request.serialization == 'xml':\r
-        data = render_to_string('objects.xml', {'container': v_container, 'objects': objects})\r
-    elif request.serialization  == 'json':\r
-        data = json.dumps(objects)\r
-    return HttpResponse(data, status = 200)\r
-\r
-@api_method('HEAD')\r
-def object_meta(request, v_account, v_container, v_object):\r
-    # Normal Response Codes: 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        info = be.get_object_meta(request.user, v_container, v_object)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    response = HttpResponse(status = 204)\r
-    response['ETag'] = info['hash']\r
-    response['Content-Length'] = info['bytes']\r
-    response['Content-Type'] = info['content_type']\r
-    response['Last-Modified'] = http_date(info['last_modified'])\r
-    # TODO: How should these be encoded for non-ascii?\r
-    for k in [x for x in info.keys() if x.startswith('X-Object-Meta-')]:\r
-        response[k.encode('utf-8')] = info[k].encode('utf-8')\r
-    \r
-    return response\r
-\r
-@api_method('GET')\r
-def object_read(request, v_account, v_container, v_object):\r
-    # Normal Response Codes: 200, 206\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       rangeNotSatisfiable (416),\r
-    #                       preconditionFailed (412),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400),\r
-    #                       notModified (304)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        info = be.get_object_meta(request.user, v_container, v_object)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    # TODO: Check if the cloudfiles python bindings expect hash/content_type/last_modified on range requests.\r
-    response = HttpResponse()\r
-    response['ETag'] = info['hash']\r
-    response['Content-Type'] = info['content_type']\r
-    response['Last-Modified'] = http_date(info['last_modified'])\r
-    \r
-    # Range handling.\r
-    range = get_range(request)\r
-    if range is not None:\r
-        offset, length = range\r
-        if length:\r
-            if offset + length > info['bytes']:\r
-                raise RangeNotSatisfiable()\r
-        else:\r
-            if offset > info['bytes']:\r
-                raise RangeNotSatisfiable()\r
-        if not length:\r
-            length = -1\r
-        \r
-        response['Content-Length'] = length        \r
-        response.status_code = 206\r
-    else:\r
-        offset = 0\r
-        length = -1\r
-        \r
-        response['Content-Length'] = info['bytes']\r
-        response.status_code = 200\r
-    \r
-    # Conditions (according to RFC2616 must be evaluated at the end).\r
-    # TODO: Check etag/date conditions.\r
-    if_match = request.META.get('HTTP_IF_MATCH')\r
-    if if_match is not None and if_match != '*':\r
-        if info['hash'] not in parse_etags(if_match):\r
-            raise PreconditionFailed()\r
-    \r
-    if_none_match = request.META.get('HTTP_IF_NONE_MATCH')\r
-    if if_none_match is not None:\r
-        if if_none_match == '*' or info['hash'] in parse_etags(if_none_match):\r
-            raise NotModified()\r
-    \r
-    if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE')\r
-    if if_modified_since is not None:\r
-        if_modified_since = parse_http_date_safe(if_modified_since)\r
-    if if_modified_since is not None and info['last_modified'] <= if_modified_since:\r
-        raise NotModified()\r
-\r
-    if_unmodified_since = request.META.get('HTTP_IF_UNMODIFIED_SINCE')\r
-    if if_unmodified_since is not None:\r
-        if_unmodified_since = parse_http_date_safe(if_unmodified_since)\r
-    if if_unmodified_since is not None and info['last_modified'] > if_unmodified_since:\r
-        raise PreconditionFailed()\r
-    \r
-    try:\r
-        response.content = be.get_object(request.user, v_container, v_object, offset, length)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    return response\r
-\r
-@api_method('PUT')\r
-def object_write(request, v_account, v_container, v_object):\r
-    # Normal Response Codes: 201\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       unprocessableEntity (422),\r
-    #                       lengthRequired (411),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    \r
-    copy_from = request.META.get('HTTP_X_COPY_FROM')\r
-    if copy_from:\r
-        parts = copy_from.split('/')\r
-        if len(parts) < 3 or parts[0] != '':\r
-            raise BadRequest('Bad X-Copy-From path.')\r
-        copy_container = parts[1]\r
-        copy_name = '/'.join(parts[2:])\r
-        \r
-        try:\r
-            info = be.get_object_meta(request.user, copy_container, copy_name)\r
-        except NameError:\r
-            raise ItemNotFound()\r
-        \r
-        content_length = request.META.get('CONTENT_LENGTH')\r
-        content_type = request.META.get('CONTENT_TYPE')\r
-        # TODO: Why is this required? Copy this ammount?\r
-        if not content_length:\r
-            raise LengthRequired()\r
-        if content_type:\r
-            info['content_type'] = content_type\r
-        \r
-        meta = get_meta(request, 'X-Object-Meta-')\r
-        info.update(meta)\r
-        \r
-        try:\r
-            be.copy_object(request.user, copy_container, copy_name, v_container, v_object)\r
-            be.update_object_meta(request.user, v_container, v_object, info)\r
-        except NameError:\r
-            raise ItemNotFound()\r
-        \r
-        response = HttpResponse(status = 201)\r
-    else:\r
-        content_length = request.META.get('CONTENT_LENGTH')\r
-        content_type = request.META.get('CONTENT_TYPE')\r
-        if not content_length or not content_type:\r
-            raise LengthRequired()\r
-        \r
-        info = {'content_type': content_type}\r
-        meta = get_meta(request, 'X-Object-Meta-')\r
-        info.update(meta)\r
-        \r
-        data = request.raw_post_data\r
-        try:\r
-            be.update_object(request.user, v_container, v_object, data)\r
-            be.update_object_meta(request.user, v_container, v_object, info)\r
-        except NameError:\r
-            raise ItemNotFound()\r
-        \r
-        # TODO: Check before update?\r
-        info = be.get_object_meta(request.user, v_container, v_object)\r
-        etag = request.META.get('HTTP_ETAG')\r
-        if etag:\r
-            etag = parse_etags(etag)[0] # TODO: Unescape properly.\r
-            if etag != info['hash']:\r
-                be.delete_object(request.user, v_container, v_object)\r
-                raise UnprocessableEntity()\r
-        \r
-        response = HttpResponse(status = 201)\r
-        response['ETag'] = info['hash']\r
-    \r
-    return response\r
-\r
-@api_method('COPY')\r
-def object_copy(request, v_account, v_container, v_object):\r
-    # Normal Response Codes: 201\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    destination = request.META.get('HTTP_DESTINATION')\r
-    if not destination:\r
-        raise BadRequest('Missing Destination.');\r
-    \r
-    parts = destination.split('/')\r
-    if len(parts) < 3 or parts[0] != '':\r
-        raise BadRequest('Bad Destination path.')\r
-    dest_container = parts[1]\r
-    dest_name = '/'.join(parts[2:])\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        info = be.get_object_meta(request.user, v_container, v_object)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    content_type = request.META.get('CONTENT_TYPE')\r
-    if content_type:\r
-        info['content_type'] = content_type\r
-    meta = get_meta(request, 'X-Object-Meta-')\r
-    info.update(meta)\r
-    \r
-    try:\r
-        be.copy_object(request.user, v_container, v_object, dest_container, dest_name)\r
-        be.update_object_meta(request.user, dest_container, dest_name, info)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    response = HttpResponse(status = 201)\r
-\r
-@api_method('POST')\r
-def object_update(request, v_account, v_container, v_object):\r
-    # Normal Response Codes: 202\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    meta = get_meta(request, 'X-Object-Meta-')\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        be.update_object_meta(request.user, v_container, v_object, meta)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    \r
-    return HttpResponse(status = 202)\r
-\r
-@api_method('DELETE')\r
-def object_delete(request, v_account, v_container, v_object):\r
-    # Normal Response Codes: 204\r
-    # Error Response Codes: serviceUnavailable (503),\r
-    #                       itemNotFound (404),\r
-    #                       unauthorized (401),\r
-    #                       badRequest (400)\r
-    \r
-    be = BackEnd(STORAGE_PATH)\r
-    try:\r
-        be.delete_object(request.user, v_container, v_object)\r
-    except NameError:\r
-        raise ItemNotFound()\r
-    return HttpResponse(status = 204)\r
-\r
-@api_method()\r
-def method_not_allowed(request):\r
-    raise BadRequest('Method not allowed.')\r