Commit dccf8009 authored by Sofia Papagiannaki's avatar Sofia Papagiannaki Committed by Giorgos Korfiatis

pithos: Add support for project originated quota

Introduce ``project`` container policy. The value of this policy denotes
the project from which the container quota originate.

Further changes:
* domain argument in get_{account|container|object}_meta backend methods
  has become optional unless user defined metadata are requested
* the pithos frontend does not query anymore the astakosclient
  for the account usage; this is handled by the backend methods
parent ffafa697
......@@ -665,15 +665,17 @@ Available policy directives:
* ``versioning``: Set to ``auto`` or ``none`` (default is ``auto``)
* ``quota``: Size limit in KB (default is ``0`` - unlimited)
* ``project``: The project origin of the container quota
If the container already exists, the operation is equal to a ``POST`` with ``update`` defined.
================ ===============================
Return Code Description
================ ===============================
201 (Created) The container has been created
202 (Accepted) The request has been accepted
================ ===============================
============================== ===============================
Return Code Description
============================== ===============================
201 (Created) The container has been created
202 (Accepted) The request has been accepted
413 (Request Entity Too Large) Insufficient quota to complete the request
============================== ===============================
POST
......@@ -705,11 +707,12 @@ To change policy, include an ``X-Container-Policy-*`` header with the name in th
To upload blocks of data to the container, set ``Content-Type`` to ``application/octet-stream`` and ``Content-Length`` to a valid value (except if using ``chunked`` as the ``Transfer-Encoding``).
================ ===============================
Return Code Description
================ ===============================
202 (Accepted) The request has been accepted
================ ===============================
============================== ===============================
Return Code Description
============================== ===============================
202 (Accepted) The request has been accepted
413 (Request Entity Too Large) Insufficient quota to complete the request
============================== ===============================
DELETE
......
......@@ -150,13 +150,14 @@ def astakos_user(user):
"name": "Firstname Lastname"}}
}
with patch('astakosclient.AstakosClient.get_quotas') as m3:
m3.return_value = {
with patch('astakosclient.AstakosClient.service_get_quotas') as m2:
m2.return_value = {user: {
"system": {
"pithos.diskspace": {
"usage": 0,
"limit": 1073741824, # 1GB
"pending": 0
}
}
}
}
......
......@@ -294,6 +294,7 @@ def account_meta(request, v_account):
getattr(request, 'token', None), groups[k])
policy = request.backend.get_account_policy(
request.user_uniq, v_account)
logger.debug(policy)
except NotAllowedError:
raise faults.Forbidden('Not allowed')
......@@ -503,6 +504,8 @@ def container_create(request, v_account, v_container):
raise faults.ItemNotFound('Container does not exist')
except ValueError:
raise faults.BadRequest('Invalid policy header')
except QuotaError, e:
raise faults.RequestEntityTooLarge('Quota error: %s' % e)
if meta:
try:
request.backend.update_container_meta(request.user_uniq, v_account,
......@@ -540,6 +543,8 @@ def container_update(request, v_account, v_container):
raise faults.ItemNotFound('Container does not exist')
except ValueError:
raise faults.BadRequest('Invalid policy header')
except QuotaError, e:
raise faults.RequestEntityTooLarge('Quota error: %s' % e)
if meta or replace:
try:
request.backend.update_container_meta(request.user_uniq, v_account,
......
......@@ -112,8 +112,8 @@ class BaseBackend(object):
"""
return []
def get_account_meta(self, user, account, domain, until=None,
include_user_defined=True, external_quota=None):
def get_account_meta(self, user, account, domain=None, until=None,
include_user_defined=True):
"""Return a dictionary with the account metadata for the domain.
The keys returned are all user-defined, except:
......@@ -127,11 +127,10 @@ class BaseBackend(object):
'until_timestamp': Last modification until the timestamp provided
'external_quota': The quota computed from external quota holder
mechanism
Raises:
NotAllowedError: Operation not permitted
ValueError: if domain is None and include_user_defined==True
"""
return {}
......@@ -242,7 +241,7 @@ class BaseBackend(object):
"""
return []
def get_container_meta(self, user, account, container, domain, until=None,
def get_container_meta(self, user, account, container, domain=None, until=None,
include_user_defined=True):
"""Return a dictionary with the container metadata for the domain.
......@@ -261,6 +260,8 @@ class BaseBackend(object):
NotAllowedError: Operation not permitted
ItemNotExists: Container does not exist
ValueError: if domain is None and include_user_defined==True
"""
return {}
......@@ -411,7 +412,7 @@ class BaseBackend(object):
"""Return a mapping of object paths to public ids under a container."""
return {}
def get_object_meta(self, user, account, container, name, domain,
def get_object_meta(self, user, account, container, name, domain=None,
version=None, include_user_defined=True):
"""Return a dictionary with the object metadata for the domain.
......@@ -444,6 +445,8 @@ class BaseBackend(object):
ItemNotExists: Container/object does not exist
VersionNotExists: Version does not exist
ValueError: if domain is None and include_user_defined==True
"""
return {}
......
"""Set container quota source
Revision ID: 4451e165da19
Revises: 3b62b3f1bf6c
Create Date: 2013-09-27 13:36:27.477141
"""
# revision identifiers, used by Alembic.
revision = '4451e165da19'
down_revision = '54dbdde2d187'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.sql import table, column, select
ROOTNODE = 0
def upgrade():
connection = op.get_bind()
nodes = table('nodes',
column('path', sa.String(2048)),
column('node', sa.Integer),
column('parent', sa.Integer))
n1 = nodes.alias('n1')
n2 = nodes.alias('n2')
policy = table('policy',
column('node', sa.Integer),
column('key', sa.String(128)),
column('value', sa.String(256)))
s = select([n2.c.node, n1.c.path])
s = s.where(n2.c.parent == n1.c.node)
s = s.where(n1.c.parent == ROOTNODE)
s = s.where(n1.c.node != ROOTNODE)
r = connection.execute(s)
rows = r.fetchall()
op.bulk_insert(policy, [{'node': node,
'key': 'project',
'value': path} for node, path in rows])
def downgrade():
pass
......@@ -278,6 +278,22 @@ class Node(DBWorker):
r.close()
return l
def node_get_parent_path(self, node):
"""Return the node's parent path.
Return None if the node is not found.
"""
n1 = self.nodes.alias('n1')
n2 = self.nodes.alias('n2')
s = select([n2.c.path])
s = s.where(n2.c.node == n1.c.parent)
s = s.where(n1.c.node == node)
r = self.conn.execute(s)
l = r.fetchone()
r.close()
return l[0] if l is not None else None
def node_get_versions(self, node, keys=(), propnames=_propnames):
"""Return the properties of all versions at node.
If keys is empty, return all properties in the order
......
......@@ -243,6 +243,17 @@ class Node(DBWorker):
self.execute(q, (node,))
return self.fetchone()
def node_get_parent_path(self, node):
"""Return the node's parent path.
Return None if the node is not found.
"""
q = ("select path from nodes as n1, nodes as n2 "
"where n2.node = n1.parent and n1.node = ?")
self.execute(q, (node,))
l = self.fetchone()
return l[0] if l is not None else None
def node_get_versions(self, node, keys=(), propnames=_propnames):
"""Return the properties of all versions at node.
If keys is empty, return all properties in the order
......
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