from pithos.api.util import parse_http_date_safe\r
\r
from pithos.api.faults import Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, LengthRequired, PreconditionFailed, RangeNotSatisfiable, UnprocessableEntity\r
-from pithos.api.util import get_object_meta, get_range, api_method\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
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
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
info = be.get_account_meta(request.user)\r
except NameError:\r
info = {'count': 0, 'bytes': 0}\r
- \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] = info[k]\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
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 == 'xml':\r
data = render_to_string('containers.xml', {'account': request.user, 'containers': containers})\r
elif request.serialization == 'json':\r
- data = json.dumps(containers) \r
+ data = json.dumps(containers)\r
return HttpResponse(data, status = 200)\r
\r
@api_method('HEAD')\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] = info[k]\r
+ \r
return response\r
\r
@api_method('PUT')\r
# itemNotFound (404),\r
# unauthorized (401),\r
# badRequest (400)\r
-\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
- return HttpResponse(status = 201)\r
+ ret = 201\r
except NameError:\r
- return HttpResponse(status = 202)\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
raise ItemNotFound()\r
return HttpResponse(status = 204)\r
\r
-# --- UP TO HERE ---\r
+# --- MERGED UP TO HERE ---\r
\r
@api_method('GET', format_allowed = True)\r
def object_list(request, v_account, v_container):\r
if not content_length or not content_type:\r
raise LengthRequired()\r
\r
- meta = get_object_meta(request)\r
+ meta = get_meta(request, 'X-Object-Meta-')\r
info = {'bytes': content_length, 'content_type': content_type, 'meta': meta}\r
\r
etag = request.META.get('HTTP_ETAG')\r
raise BadRequest('Bad Destination path.')\r
dest_container = parts[1]\r
dest_name = '/'.join(parts[2:])\r
- \r
+ \r
info = get_object_meta(request.user, v_container, v_object)\r
- \r
+ \r
content_type = request.META.get('CONTENT_TYPE')\r
if content_type:\r
info['content_type'] = content_type\r
\r
- meta = get_object_meta(request)\r
+ meta = get_meta(request, 'X-Object-Meta-')\r
for k, v in meta.iteritems():\r
info['meta'][k] = v\r
\r
# unauthorized (401),\r
# badRequest (400)\r
\r
- meta = get_object_meta(request)\r
+ meta = get_meta(request, 'X-Object-Meta-')\r
\r
update_object_meta(request.user, v_container, v_object, meta)\r
return HttpResponse(status = 202)\r
import json\r
import logging\r
import types\r
+import hashlib\r
+\r
+logger = logging.getLogger(__name__)\r
+formatter = logging.Formatter('[%(levelname)s] %(message)s')\r
+handler = logging.FileHandler('backend.out')\r
+handler.setFormatter(formatter)\r
+logger.addHandler(handler)\r
\r
class BackEnd:\r
+\r
+ logger = None\r
+ \r
def __init__(self, basepath, log_file='backend.out', log_level=logging.DEBUG):\r
self.basepath = basepath\r
\r
- self.logger = logging.getLogger(__name__)\r
- self.logger.setLevel(log_level)\r
- formatter = logging.Formatter('[%(levelname)s] %(message)s')\r
- handler = logging.FileHandler(log_file)\r
- handler.setFormatter(formatter)\r
- self.logger.addHandler(handler)\r
+ # TODO: Manage log_file.\r
+ logger.setLevel(log_level)\r
\r
if not os.path.exists(basepath):\r
os.makedirs(basepath)\r
sql = '''create table if not exists metadata(object_id int, name text, value text)'''\r
self.con.execute(sql)\r
self.con.commit()\r
-\r
+ \r
+ # TODO: Create/delete account?\r
+ \r
def get_account_meta(self, account):\r
"""\r
- returns a dictionary with the container metadata\r
+ returns a dictionary with the account metadata\r
"""\r
- self.logger.debug("get_account_meta: %s", account)\r
+ logger.debug("get_account_meta: %s", account)\r
fullname = os.path.join(self.basepath, account)\r
if not os.path.exists(fullname):\r
raise NameError('Account does not exist')\r
- contents = os.listdir(fullname) \r
+ contents = os.listdir(fullname)\r
count = len(contents)\r
size = sum(os.path.getsize(os.path.join(self.basepath, account, objectname)) for objectname in contents)\r
- return {'name': account, 'count': count, 'bytes': size}\r
- \r
+ meta = self.__get_metadata(account)\r
+ meta.update({'name': account, 'count': count, 'bytes': size})\r
+ return meta\r
+\r
+ def update_account_meta(self, account, meta):\r
+ """\r
+ updates the metadata associated with the account\r
+ """\r
+ logger.debug("update_account_meta: %s %s", account, meta)\r
+ fullname = os.path.join(self.basepath, account)\r
+ if not os.path.exists(fullname):\r
+ os.makedirs(fullname)\r
+ self.__put_metadata(account, meta)\r
+ return\r
+ \r
def create_container(self, account, name):\r
"""\r
creates a new container with the given name\r
if it doesn't exist under the basepath\r
"""\r
- self.logger.debug("create_container: %s %s", account, name)\r
+ logger.debug("create_container: %s %s", account, name)\r
fullname = os.path.join(self.basepath, account, name)\r
if not os.path.exists(fullname):\r
os.makedirs(fullname)\r
else:\r
raise NameError('Container already exists')\r
return\r
-\r
+ \r
def delete_container(self, account, name):\r
"""\r
deletes the container with the given name\r
if it exists under the basepath and is empty\r
"""\r
- self.logger.debug("delete_container: %s %s", account, name)\r
+ logger.debug("delete_container: %s %s", account, name)\r
fullname = os.path.join(self.basepath, account, name)\r
if not os.path.exists(fullname):\r
raise NameError('Container does not exist')\r
raise Exception('Container is not empty')\r
else:\r
os.rmdir(fullname)\r
+ self.__del_dbpath(os.path.join(account, name))\r
return\r
\r
def get_container_meta(self, account, name):\r
"""\r
returns a dictionary with the container metadata\r
"""\r
- self.logger.debug("get_container_meta: %s %s", account, name)\r
+ logger.debug("get_container_meta: %s %s", account, name)\r
fullname = os.path.join(self.basepath, account, name)\r
if not os.path.exists(fullname):\r
raise NameError('Container does not exist')\r
- contents = os.listdir(fullname) \r
+ contents = os.listdir(fullname)\r
count = len(contents)\r
size = sum(os.path.getsize(os.path.join(self.basepath, account, name, objectname)) for objectname in contents)\r
- return {'name': name, 'count': count, 'bytes': size}\r
+ meta = self.__get_metadata(os.path.join(account, name))\r
+ meta.update({'name': name, 'count': count, 'bytes': size})\r
+ return meta\r
+ \r
+ def update_container_meta(self, account, name, meta):\r
+ """\r
+ updates the metadata associated with the container\r
+ """\r
+ logger.debug("update_container_meta: %s %s %s", account, name, meta)\r
+ fullname = os.path.join(self.basepath, account, name)\r
+ if not os.path.exists(fullname):\r
+ raise NameError('Container does not exist')\r
+ self.__put_metadata(os.path.join(account, name), meta)\r
+ return\r
\r
def list_containers(self, account, marker = None, limit = 10000):\r
"""\r
returns a list of at most limit (default = 10000) containers \r
starting from the next item after the optional marker\r
"""\r
- self.logger.debug("list_containers: %s %s %s", account, marker, limit)\r
+ logger.debug("list_containers: %s %s %s", account, marker, limit)\r
fullname = os.path.join(self.basepath, account)\r
if not os.path.exists(fullname):\r
raise NameError('Account does not exist')\r
- containers = os.listdir(fullname) \r
+ containers = os.listdir(fullname)\r
start = 0\r
if marker:\r
try:\r
pass\r
return containers[start:limit]\r
\r
- # --- UP TO HERE ---\r
+ \r
+ \r
+# def __get_linkinfo(self, path):\r
+# c = self.con.execute('select rowid from objects where name=''?''', (path,))\r
+# row = c.fetchone()\r
+# if row:\r
+# return str(row[0])\r
+# else:\r
+# raise NameError('Requested path not found')\r
+# \r
+# def __put_linkinfo(self, path):\r
+# id = self.con.execute('insert into objects (name) values (?)', (path,)).lastrowid\r
+# self.con.commit()\r
+# return id\r
+ \r
+ \r
+ \r
+ def __del_dbpath(self, path):\r
+ self.con.execute('delete from metadata where object_id in (select rowid from objects where name = ''?'')', (path,))\r
+ self.con.execute('delete from objects where name = ''?''', (path,))\r
+ self.con.commit()\r
+ return\r
+ \r
+ def __get_metadata(self, path):\r
+ c = self.con.execute('select m.name, m.value from metadata m, objects o where o.rowid = m.object_id and o.name = ''?''', (path,))\r
+ return dict(c.fetchall())\r
+ \r
+ def __put_metadata(self, path, meta):\r
+ c = self.con.execute('select rowid from objects where name=''?''', (path,))\r
+ row = c.fetchone()\r
+ if row:\r
+ link = str(row[0])\r
+ else:\r
+ link = self.con.execute('insert into objects (name) values (?)', (path,)).lastrowid \r
+ for k, v in meta.iteritems():\r
+ self.con.execute('insert or replace into metadata (object_id, name, value) values (?, ?, ?)', (link, k, v))\r
+ self.con.commit()\r
+ return\r
+ \r
+ def __object_hash(self, location, block_size = 8192):\r
+ md5 = hashlib.md5()\r
+ f = open(location, 'r')\r
+ while True:\r
+ data = f.read(block_size)\r
+ if not data:\r
+ break\r
+ md5.update(data)\r
+ f.close()\r
+ return md5.hexdigest()\r
+ \r
+ # --- MERGED UP TO HERE ---\r
\r
def list_objects(self, account, container, prefix='', delimiter=None, marker = None, limit = 10000):\r
"""\r
returns a list of the objects existing under a specific account container\r
"""\r
- self.logger.info("list_objects: %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit)\r
+ logger.info("list_objects: %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit)\r
dir = os.path.join(self.basepath, account, container)\r
if not os.path.exists(dir):\r
raise NameError('Container does not exist')\r
f = open(location, 'w')\r
f.write(data)\r
f.close()\r
- \r
+ \r
def __delete_data(self, location, account, container):\r
file = os.path.join(self.basepath, account, container, location)\r
if not os.path.exists(dir):\r