Commit 94195dd0 authored by Antony Chazapis's avatar Antony Chazapis
Browse files

Containers API.

parent 2e55b5fe
......@@ -7,11 +7,13 @@ from django.template.loader import render_to_string
from django.utils import simplejson as json
from pithos.api.faults import Fault, BadRequest, Unauthorized
from pithos.api.util import api_method, binary_search_name
from pithos.api.util import api_method
from pithos.backends.dummy_debug import *
import logging
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.DEBUG)
@api_method('GET')
def authenticate(request):
......@@ -25,9 +27,6 @@ def authenticate(request):
if not x_auth_user or not x_auth_key:
raise BadRequest('Missing auth user or key.')
# TODO: Authenticate.
#if x_auth_user == "test":
# raise Unauthorized()
response = HttpResponse(status = 204)
response['X-Auth-Token'] = 'eaaafd18-0fed-4b3a-81b4-663c99ec1cbb'
......@@ -56,7 +55,6 @@ def container_demux(request, v_account, v_container):
return method_not_allowed(request)
def object_demux(request, v_account, v_container, v_object):
# TODO: Check parameter sizes.
if request.method == 'HEAD':
return object_meta(request, v_account, v_container, v_object)
elif request.method == 'GET':
......@@ -72,7 +70,18 @@ def object_demux(request, v_account, v_container, v_object):
@api_method('HEAD')
def account_meta(request, v_account):
return HttpResponse("account_meta: %s" % v_account)
# Normal Response Codes: 204
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404),
# unauthorized (401),
# badRequest (400)
container_count, bytes_count = get_account_meta(request.user)
response = HttpResponse(status = 204)
response['X-Account-Container-Count'] = container_count
response['X-Account-Total-Bytes-Used'] = bytes_count
return response
@api_method('GET', format_allowed = True)
def container_list(request, v_account):
......@@ -81,60 +90,111 @@ def container_list(request, v_account):
# unauthorized (401),
# badRequest (400)
containers = [
{'name': '1', 'count': 2, 'bytes': 123},
{'name': '2', 'count': 22, 'bytes': 245},
{'name': '3', 'count': 222, 'bytes': 83745},
{'name': 'four', 'count': 2222, 'bytes': 274365}
]
if len(containers) == 0:
return HttpResponse(status = 204)
limit = request.GET.get('limit')
marker = request.GET.get('marker')
start = 0
if marker:
try:
start = binary_search_name(containers, marker) + 1
except ValueError:
pass
limit = request.GET.get('limit')
if limit:
try:
limit = int(limit)
except ValueError:
limit = None
if not limit or limit > 10000:
limit = 10000
containers = containers[start:start + limit]
containers = list_containers(request.user, marker, limit)
if len(containers) == 0:
return HttpResponse(status = 204)
if request.serialization == 'xml':
# TODO: The xml must include the account name as well.
data = render_to_string('containers.xml', {'containers': containers})
data = render_to_string('containers.xml', {'account': request.user, 'containers': containers})
elif request.serialization == 'json':
data = json.dumps(containers)
else:
data = '\n'.join(x['name'] for x in containers)
# TODO: Return 404 when the account is not found.
return HttpResponse(data, status = 200)
@api_method('HEAD')
def container_meta(request, v_account, v_container):
return HttpResponse("container_meta: %s %s" % (v_account, v_container))
# Normal Response Codes: 204
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404),
# unauthorized (401),
# badRequest (400)
object_count, bytes_count = get_container_meta(request.user, v_container)
response = HttpResponse(status = 204)
response['X-Container-Object-Count'] = object_count
response['X-Container-Bytes-Used'] = bytes_count
return response
@api_method('PUT')
def container_create(request, v_account, v_container):
return HttpResponse("container_create: %s %s" % (v_account, v_container))
# Normal Response Codes: 201, 202
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404),
# unauthorized (401),
# badRequest (400)
if create_container(request.user, v_container):
return HttpResponse(status = 201)
else:
return HttpResponse(status = 202)
@api_method('DELETE')
def container_delete(request, v_account, v_container):
return HttpResponse("container_delete: %s %s" % (v_account, v_container))
# Normal Response Codes: 204
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404),
# unauthorized (401),
# badRequest (400)
object_count, bytes_count = get_container_meta(request.user, v_container)
if object_count > 0:
return HttpResponse(status = 409)
delete_container(request.user, v_container)
return HttpResponse(status = 204)
@api_method('GET')
@api_method('GET', format_allowed = True)
def object_list(request, v_account, v_container):
return HttpResponse("object_list: %s %s" % (v_account, v_container))
# Normal Response Codes: 200, 204
# Error Response Codes: serviceUnavailable (503),
# itemNotFound (404),
# unauthorized (401),
# badRequest (400)
path = request.GET.get('path')
prefix = request.GET.get('prefix')
delimiter = request.GET.get('delimiter')
logging.debug("path: %s", path)
# Path overrides prefix and delimiter.
if path:
prefix = path
delimiter = '/'
# Naming policy.
if prefix and delimiter:
prefix = prefix + delimiter
marker = request.GET.get('marker')
limit = request.GET.get('limit')
if limit:
try:
limit = int(limit)
except ValueError:
limit = None
objects = list_objects(request.user, v_container, prefix, delimiter, marker, limit)
if len(objects) == 0:
return HttpResponse(status = 204)
if request.serialization == 'xml':
data = render_to_string('objects.xml', {'container': v_container, 'objects': objects})
elif request.serialization == 'json':
data = json.dumps(objects)
else:
data = '\n'.join(x['name'] for x in objects)
return HttpResponse(data, status = 200)
@api_method('HEAD')
def object_meta(request, v_account, v_container, v_object):
......
{% spaceless %}
<?xml version="1.0" encoding="UTF-8"?>
<account name="">
<account name="{{ account }}">
{% for container in containers %}
<container>
<name>{{ container.name }}</name>
<count>{{ container.count }}</count>
<bytes>{{ container.bytes }}</bytes>
</container>
{% endfor %}
</account>
......
{% spaceless %}
<?xml version="1.0" encoding="UTF-8"?>
<container name="{{ container }}">
{% for object in objects %}
<object>
<name>{{ object.name }}</name>
<hash>{{ object.hash }}</hash>
<bytes>{{ object.bytes }}</bytes>
<content_type>{{ object.content_type }}</content_type>
<last_modified>{{ object.last_modified }}</last_modified>
</object>
{% endfor %}
</container>
{% endspaceless %}
......@@ -22,20 +22,6 @@ import datetime
import dateutil.parser
import logging
def binary_search_name(a, x, lo = 0, hi = None):
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
midval = a[mid]['name']
if midval < x:
lo = mid + 1
elif midval > x:
hi = mid
else:
return mid
raise ValueError()
# class UTC(tzinfo):
# def utcoffset(self, dt):
# return timedelta(0)
......@@ -173,7 +159,7 @@ def render_fault(request, fault):
# data = json.dumps(d)
# resp = HttpResponse(data, status=fault.code)
resp = HttpResponse(status=fault.code)
resp = HttpResponse(status = fault.code)
update_response_headers(request, resp)
return resp
......@@ -209,6 +195,10 @@ def api_method(http_method = None, format_allowed = False):
def wrapper(request, *args, **kwargs):
try:
request.serialization = request_serialization(request, format_allowed)
# TODO: Authenticate.
# TODO: Return 401/404 when the account is not found.
request.user = "test"
# TODO: Check parameter sizes.
if http_method and request.method != http_method:
raise BadRequest('Method not allowed.')
......
"""
Dummy backend with debugging output
A backend with no functionality other than producing debugging output.
"""
import logging
def binary_search_name(a, x, lo = 0, hi = None):
"""
Search a sorted array of dicts for the value of the key 'name'.
Raises ValueError if the value is not found.
a -- the array
x -- the value to search for
"""
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
midval = a[mid]['name']
if midval < x:
lo = mid + 1
elif midval > x:
hi = mid
else:
return mid
raise ValueError()
def get_account_meta(account):
logging.debug("get_account_meta: %s %s", account, name)
return {'count': 13, 'bytes': 3148237468}
def create_container(account, name):
"""
Returns True if the container was created, False if it already exists.
"""
logging.debug("create_container: %s %s", account, name)
return True
def delete_container(account, name):
logging.debug("delete_container: %s %s", account, name)
return
def get_container_meta(account, name):
logging.debug("get_container_meta: %s %s", account, name)
return {'count': 22, 'bytes': 245}
def list_containers(account, marker = None, limit = 10000):
logging.debug("list_containers: %s %s %s", account, marker, limit)
containers = [
{'name': '1', 'count': 2, 'bytes': 123},
{'name': '2', 'count': 22, 'bytes': 245},
{'name': '3', 'count': 222, 'bytes': 83745},
{'name': 'four', 'count': 2222, 'bytes': 274365}
]
start = 0
if marker:
try:
start = binary_search_name(containers, marker) + 1
except ValueError:
pass
if not limit or limit > 10000:
limit = 10000
return containers[start:start + limit]
def list_objects(account, container, prefix = None, delimiter = None, marker = None, limit = 10000):
logging.debug("list_objects: %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit)
objects = [
{'name': 'other', 'hash': 'dfgs', 'bytes': 0, 'content_type': 'application/directory', 'last_modified': 23453454},
{'name': 'other/something', 'hash': 'vkajf', 'bytes': 234, 'content_type': 'application/octet-stream', 'last_modified': 878434562},
{'name': 'photos', 'hash': 'kajdsn', 'bytes': 0, 'content_type': 'application/directory', 'last_modified': 1983274},
{'name': 'photos/asdf', 'hash': 'jadsfkj', 'bytes': 0, 'content_type': 'application/directory', 'last_modified': 378465873},
{'name': 'photos/asdf/test', 'hash': 'sudfhius', 'bytes': 37284, 'content_type': 'text/plain', 'last_modified': 93674212},
{'name': 'photos/me.jpg', 'hash': 'sdgsdfgsf', 'bytes': 534, 'content_type': 'image/jpeg', 'last_modified': 262345345},
{'name': 'photos/text.txt', 'hash': 'asdfasd', 'bytes': 34243, 'content_type': 'text/plain', 'last_modified': 45345345}
]
if prefix or delimiter:
if prefix:
objects = [x for x in objects if x['name'].startswith(prefix)]
if delimiter:
pseudo_objects = {}
for x in objects:
pseudo_name = x['name'][len(prefix):]
i = pseudo_name.find(delimiter)
if i != -1:
pseudo_name = pseudo_name[:i]
# TODO: Virtual directories.
if pseudo_name not in pseudo_objects:
pseudo_objects[pseudo_name] = x
objects = sorted(pseudo_objects.values(), key=lambda o: o['name'])
start = 0
if marker:
try:
start = binary_search_name(objects, marker) + 1
except ValueError:
pass
if not limit or limit > 10000:
limit = 10000
return objects[start:start + limit]
def get_object_meta(container, name):
return {'name': name, 'hash': '', 'bytes': 0}
def get_object_data(container, name, offset=0, length=0):
return ''
def update_object_data(container, name, meta, data):
return
def update_object_meta(container, name, meta):
return
def copy_object(container, name, new_name):
return
def delete_object(container, name):
return
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment