Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ fff37615

History | View | Annotate | Download (61.7 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
3 a9b3f29d Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 a9b3f29d Antony Chazapis
# without modification, are permitted provided that the following
5 a9b3f29d Antony Chazapis
# conditions are met:
6 2715ade4 Sofia Papagiannaki
#
7 a9b3f29d Antony Chazapis
#   1. Redistributions of source code must retain the above
8 a9b3f29d Antony Chazapis
#      copyright notice, this list of conditions and the following
9 a9b3f29d Antony Chazapis
#      disclaimer.
10 2715ade4 Sofia Papagiannaki
#
11 7ff57991 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 7ff57991 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 a9b3f29d Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 a9b3f29d Antony Chazapis
#      provided with the distribution.
15 2715ade4 Sofia Papagiannaki
#
16 a9b3f29d Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a9b3f29d Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a9b3f29d Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a9b3f29d Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a9b3f29d Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a9b3f29d Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a9b3f29d Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a9b3f29d Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a9b3f29d Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a9b3f29d Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a9b3f29d Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a9b3f29d Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 2715ade4 Sofia Papagiannaki
#
29 a9b3f29d Antony Chazapis
# The views and conclusions contained in the software and
30 a9b3f29d Antony Chazapis
# documentation are those of the authors and should not be
31 a9b3f29d Antony Chazapis
# interpreted as representing official policies, either expressed
32 a9b3f29d Antony Chazapis
# or implied, of GRNET S.A.
33 a9b3f29d Antony Chazapis
34 2c5363a0 Antony Chazapis
import sys
35 a9b3f29d Antony Chazapis
import os
36 a9b3f29d Antony Chazapis
import time
37 37bee317 Antony Chazapis
import uuid as uuidlib
38 a9b3f29d Antony Chazapis
import logging
39 6e147ecc Antony Chazapis
import hashlib
40 a9b3f29d Antony Chazapis
import binascii
41 a9b3f29d Antony Chazapis
42 b3119c3e Georgios D. Tsoukalas
from kamaki.clients.quotaholder import QuotaholderClient
43 0307b47f Georgios D. Tsoukalas
44 7efc9f86 Sofia Papagiannaki
from base import DEFAULT_QUOTA, DEFAULT_VERSIONING, NotAllowedError, QuotaError, BaseBackend, \
45 7efc9f86 Sofia Papagiannaki
    AccountExists, ContainerExists, AccountNotEmpty, ContainerNotEmpty, ItemNotExists, VersionNotExists
46 a9b3f29d Antony Chazapis
47 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
48 2715ade4 Sofia Papagiannaki
49 2715ade4 Sofia Papagiannaki
50 6e147ecc Antony Chazapis
class HashMap(list):
51 6e147ecc Antony Chazapis
52 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
53 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
54 6e147ecc Antony Chazapis
        self.blocksize = blocksize
55 6e147ecc Antony Chazapis
        self.blockhash = blockhash
56 6e147ecc Antony Chazapis
57 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
58 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
59 6e147ecc Antony Chazapis
        h.update(v)
60 6e147ecc Antony Chazapis
        return h.digest()
61 6e147ecc Antony Chazapis
62 6e147ecc Antony Chazapis
    def hash(self):
63 6e147ecc Antony Chazapis
        if len(self) == 0:
64 6e147ecc Antony Chazapis
            return self._hash_raw('')
65 6e147ecc Antony Chazapis
        if len(self) == 1:
66 6e147ecc Antony Chazapis
            return self.__getitem__(0)
67 6e147ecc Antony Chazapis
68 6e147ecc Antony Chazapis
        h = list(self)
69 6e147ecc Antony Chazapis
        s = 2
70 6e147ecc Antony Chazapis
        while s < len(h):
71 6e147ecc Antony Chazapis
            s = s * 2
72 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
73 6e147ecc Antony Chazapis
        while len(h) > 1:
74 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
75 6e147ecc Antony Chazapis
        return h[0]
76 5a96180b Antony Chazapis
77 228de81b Antony Chazapis
# Default modules and settings.
78 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
79 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
80 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
81 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
82 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
83 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
84 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
85 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
86 fa9cae7e Antony Chazapis
87 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
88 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
89 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
90 228de81b Antony Chazapis
91 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
92 44ad5860 Antony Chazapis
93 44ad5860 Antony Chazapis
inf = float('inf')
94 44ad5860 Antony Chazapis
95 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
96 bb4eafc6 Antony Chazapis
97 a9b3f29d Antony Chazapis
98 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
99 a9b3f29d Antony Chazapis
100 1c2fc0ff Antony Chazapis
101 a9b3f29d Antony Chazapis
def backend_method(func=None, autocommit=1):
102 a9b3f29d Antony Chazapis
    if func is None:
103 a9b3f29d Antony Chazapis
        def fn(func):
104 a9b3f29d Antony Chazapis
            return backend_method(func, autocommit)
105 a9b3f29d Antony Chazapis
        return fn
106 a9b3f29d Antony Chazapis
107 a9b3f29d Antony Chazapis
    if not autocommit:
108 a9b3f29d Antony Chazapis
        return func
109 2715ade4 Sofia Papagiannaki
110 a9b3f29d Antony Chazapis
    def fn(self, *args, **kw):
111 2c5363a0 Antony Chazapis
        self.wrapper.execute()
112 ecd01afd root
        serials = []
113 ecd01afd root
        self.serials = serials
114 85352a77 root
        self.messages = []
115 8313c681 root
116 a9b3f29d Antony Chazapis
        try:
117 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
118 39ef6f41 Antony Chazapis
            for m in self.messages:
119 39ef6f41 Antony Chazapis
                self.queue.send(*m)
120 5578064f root
            if serials:
121 5578064f root
                self.quotaholder.accept_commission(
122 5578064f root
                            context     =   {},
123 5578064f root
                            clientkey   =   'pithos',
124 5578064f root
                            serials     =   serials)
125 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
126 a9b3f29d Antony Chazapis
            return ret
127 a9b3f29d Antony Chazapis
        except:
128 73fbe301 Sofia Papagiannaki
            if serials:
129 73fbe301 Sofia Papagiannaki
                self.quotaholder.reject_commission(
130 73fbe301 Sofia Papagiannaki
                            context     =   {},
131 73fbe301 Sofia Papagiannaki
                            clientkey   =   'pithos',
132 73fbe301 Sofia Papagiannaki
                            serials     =   serials)
133 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
134 a9b3f29d Antony Chazapis
            raise
135 a9b3f29d Antony Chazapis
    return fn
136 a9b3f29d Antony Chazapis
137 a9b3f29d Antony Chazapis
138 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
139 a9b3f29d Antony Chazapis
    """A modular backend.
140 2715ade4 Sofia Papagiannaki

141 e9363f82 Antony Chazapis
    Uses modules for SQL functions and storage.
142 a9b3f29d Antony Chazapis
    """
143 2715ade4 Sofia Papagiannaki
144 46286f5f Antony Chazapis
    def __init__(self, db_module=None, db_connection=None,
145 f3b65e8f Antony Chazapis
                 block_module=None, block_path=None, block_umask=None,
146 3173699c Sofia Papagiannaki
                 queue_module=None, queue_hosts=None, queue_exchange=None,
147 3173699c Sofia Papagiannaki
                 quotaholder_url=None, quotaholder_token=None,
148 b1dadd0e Sofia Papagiannaki
                 free_versioning=True):
149 228de81b Antony Chazapis
        db_module = db_module or DEFAULT_DB_MODULE
150 228de81b Antony Chazapis
        db_connection = db_connection or DEFAULT_DB_CONNECTION
151 228de81b Antony Chazapis
        block_module = block_module or DEFAULT_BLOCK_MODULE
152 228de81b Antony Chazapis
        block_path = block_path or DEFAULT_BLOCK_PATH
153 f3b65e8f Antony Chazapis
        block_umask = block_umask or DEFAULT_BLOCK_UMASK
154 46286f5f Antony Chazapis
        #queue_module = queue_module or DEFAULT_QUEUE_MODULE
155 f4fbb0fa Sofia Papagiannaki
        #queue_hosts = queue_hosts or DEFAULT_QUEUE_HOSTS
156 f4fbb0fa Sofia Papagiannaki
        #queue_exchange = queue_exchange or DEFAULT_QUEUE_EXCHANGE
157 f81e20b0 Giorgos Verigakis
        
158 a9b3f29d Antony Chazapis
        self.hash_algorithm = 'sha256'
159 2715ade4 Sofia Papagiannaki
        self.block_size = 4 * 1024 * 1024  # 4MB
160 b1dadd0e Sofia Papagiannaki
        self.free_versioning = free_versioning
161 2715ade4 Sofia Papagiannaki
162 2715ade4 Sofia Papagiannaki
        self.default_policy = {'quota': DEFAULT_QUOTA,
163 2715ade4 Sofia Papagiannaki
                               'versioning': DEFAULT_VERSIONING}
164 2715ade4 Sofia Papagiannaki
165 46286f5f Antony Chazapis
        def load_module(m):
166 46286f5f Antony Chazapis
            __import__(m)
167 46286f5f Antony Chazapis
            return sys.modules[m]
168 2715ade4 Sofia Papagiannaki
169 46286f5f Antony Chazapis
        self.db_module = load_module(db_module)
170 46286f5f Antony Chazapis
        self.wrapper = self.db_module.DBWrapper(db_connection)
171 e9363f82 Antony Chazapis
        params = {'wrapper': self.wrapper}
172 e9363f82 Antony Chazapis
        self.permissions = self.db_module.Permissions(**params)
173 2c690fe9 Sofia Papagiannaki
        self.config = self.db_module.Config(**params)
174 cb787cc4 Sofia Papagiannaki
        self.quotaholder_serials = self.db_module.QuotaholderSerial(**params)
175 e9363f82 Antony Chazapis
        for x in ['READ', 'WRITE']:
176 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
177 e9363f82 Antony Chazapis
        self.node = self.db_module.Node(**params)
178 33b4e4a6 Antony Chazapis
        for x in ['ROOTNODE', 'SERIAL', 'HASH', 'SIZE', 'TYPE', 'MTIME', 'MUSER', 'UUID', 'CHECKSUM', 'CLUSTER', 'MATCH_PREFIX', 'MATCH_EXACT']:
179 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
180 2715ade4 Sofia Papagiannaki
181 46286f5f Antony Chazapis
        self.block_module = load_module(block_module)
182 7ca7bb08 Antony Chazapis
        params = {'path': block_path,
183 7ca7bb08 Antony Chazapis
                  'block_size': self.block_size,
184 f3b65e8f Antony Chazapis
                  'hash_algorithm': self.hash_algorithm,
185 f3b65e8f Antony Chazapis
                  'umask': block_umask}
186 7ca7bb08 Antony Chazapis
        self.store = self.block_module.Store(**params)
187 46286f5f Antony Chazapis
188 f4fbb0fa Sofia Papagiannaki
        if queue_module and queue_hosts:
189 46286f5f Antony Chazapis
            self.queue_module = load_module(queue_module)
190 f4fbb0fa Sofia Papagiannaki
            params = {'hosts': queue_hosts,
191 f4fbb0fa Sofia Papagiannaki
                              'exchange': queue_exchange,
192 fa9cae7e Antony Chazapis
                      'client_id': QUEUE_CLIENT_ID}
193 46286f5f Antony Chazapis
            self.queue = self.queue_module.Queue(**params)
194 46286f5f Antony Chazapis
        else:
195 46286f5f Antony Chazapis
            class NoQueue:
196 1a239dc1 Sofia Papagiannaki
                def send(self, *args):
197 46286f5f Antony Chazapis
                    pass
198 2715ade4 Sofia Papagiannaki
199 b9a8feec root
                def close(self):
200 b9a8feec root
                    pass
201 2715ade4 Sofia Papagiannaki
202 46286f5f Antony Chazapis
            self.queue = NoQueue()
203 2715ade4 Sofia Papagiannaki
204 7ed99da8 root
        self.quotaholder_url = quotaholder_url
205 3173699c Sofia Papagiannaki
        self.quotaholder_token = quotaholder_token
206 3173699c Sofia Papagiannaki
        self.quotaholder = QuotaholderClient(quotaholder_url, quotaholder_token)
207 7ed99da8 root
        self.serials = []
208 a7f7699d root
        self.messages = []
209 0307b47f Georgios D. Tsoukalas
210 d14fe290 Antony Chazapis
    def close(self):
211 d14fe290 Antony Chazapis
        self.wrapper.close()
212 b9a8feec root
        self.queue.close()
213 2715ade4 Sofia Papagiannaki
214 a9b3f29d Antony Chazapis
    @backend_method
215 a9b3f29d Antony Chazapis
    def list_accounts(self, user, marker=None, limit=10000):
216 a9b3f29d Antony Chazapis
        """Return a list of accounts the user can access."""
217 2715ade4 Sofia Papagiannaki
218 02c4d2ba Antony Chazapis
        logger.debug("list_accounts: %s %s %s", user, marker, limit)
219 a9b3f29d Antony Chazapis
        allowed = self._allowed_accounts(user)
220 a9b3f29d Antony Chazapis
        start, limit = self._list_limits(allowed, marker, limit)
221 a9b3f29d Antony Chazapis
        return allowed[start:start + limit]
222 2715ade4 Sofia Papagiannaki
223 a9b3f29d Antony Chazapis
    @backend_method
224 82482e2c Antony Chazapis
    def get_account_meta(self, user, account, domain, until=None, include_user_defined=True):
225 cb69c154 Antony Chazapis
        """Return a dictionary with the account metadata for the domain."""
226 2715ade4 Sofia Papagiannaki
227 2715ade4 Sofia Papagiannaki
        logger.debug(
228 2715ade4 Sofia Papagiannaki
            "get_account_meta: %s %s %s %s", user, account, domain, until)
229 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, user == account)
230 a9b3f29d Antony Chazapis
        if user != account:
231 44ad5860 Antony Chazapis
            if until or node is None or account not in self._allowed_accounts(user):
232 a9b3f29d Antony Chazapis
                raise NotAllowedError
233 a9b3f29d Antony Chazapis
        try:
234 44ad5860 Antony Chazapis
            props = self._get_properties(node, until)
235 2c5363a0 Antony Chazapis
            mtime = props[self.MTIME]
236 a9b3f29d Antony Chazapis
        except NameError:
237 62f915a1 Antony Chazapis
            props = None
238 a9b3f29d Antony Chazapis
            mtime = until
239 62f915a1 Antony Chazapis
        count, bytes, tstamp = self._get_statistics(node, until)
240 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
241 a9b3f29d Antony Chazapis
        if until is None:
242 a9b3f29d Antony Chazapis
            modified = tstamp
243 a9b3f29d Antony Chazapis
        else:
244 2715ade4 Sofia Papagiannaki
            modified = self._get_statistics(
245 2715ade4 Sofia Papagiannaki
                node)[2]  # Overall last modification.
246 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
247 2715ade4 Sofia Papagiannaki
248 a9b3f29d Antony Chazapis
        if user != account:
249 a9b3f29d Antony Chazapis
            meta = {'name': account}
250 a9b3f29d Antony Chazapis
        else:
251 44ad5860 Antony Chazapis
            meta = {}
252 82482e2c Antony Chazapis
            if props is not None and include_user_defined:
253 2715ade4 Sofia Papagiannaki
                meta.update(
254 2715ade4 Sofia Papagiannaki
                    dict(self.node.attribute_get(props[self.SERIAL], domain)))
255 a9b3f29d Antony Chazapis
            if until is not None:
256 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
257 44ad5860 Antony Chazapis
            meta.update({'name': account, 'count': count, 'bytes': bytes})
258 a9b3f29d Antony Chazapis
        meta.update({'modified': modified})
259 a9b3f29d Antony Chazapis
        return meta
260 2715ade4 Sofia Papagiannaki
261 a9b3f29d Antony Chazapis
    @backend_method
262 cb69c154 Antony Chazapis
    def update_account_meta(self, user, account, domain, meta, replace=False):
263 cb69c154 Antony Chazapis
        """Update the metadata associated with the account for the domain."""
264 2715ade4 Sofia Papagiannaki
265 2715ade4 Sofia Papagiannaki
        logger.debug("update_account_meta: %s %s %s %s %s", user,
266 2715ade4 Sofia Papagiannaki
                     account, domain, meta, replace)
267 a9b3f29d Antony Chazapis
        if user != account:
268 a9b3f29d Antony Chazapis
            raise NotAllowedError
269 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, True)
270 4819d34f Antony Chazapis
        self._put_metadata(user, node, domain, meta, replace)
271 2715ade4 Sofia Papagiannaki
272 a9b3f29d Antony Chazapis
    @backend_method
273 a9b3f29d Antony Chazapis
    def get_account_groups(self, user, account):
274 a9b3f29d Antony Chazapis
        """Return a dictionary with the user groups defined for this account."""
275 2715ade4 Sofia Papagiannaki
276 fe2db49d Sofia Papagiannaki
        logger.debug("get_account_groups: %s %s", user, account)
277 a9b3f29d Antony Chazapis
        if user != account:
278 a9b3f29d Antony Chazapis
            if account not in self._allowed_accounts(user):
279 a9b3f29d Antony Chazapis
                raise NotAllowedError
280 a9b3f29d Antony Chazapis
            return {}
281 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
282 0f9d752c Antony Chazapis
        return self.permissions.group_dict(account)
283 2715ade4 Sofia Papagiannaki
284 a9b3f29d Antony Chazapis
    @backend_method
285 a9b3f29d Antony Chazapis
    def update_account_groups(self, user, account, groups, replace=False):
286 a9b3f29d Antony Chazapis
        """Update the groups associated with the account."""
287 2715ade4 Sofia Papagiannaki
288 2715ade4 Sofia Papagiannaki
        logger.debug("update_account_groups: %s %s %s %s", user,
289 2715ade4 Sofia Papagiannaki
                     account, groups, replace)
290 a9b3f29d Antony Chazapis
        if user != account:
291 a9b3f29d Antony Chazapis
            raise NotAllowedError
292 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
293 a9b3f29d Antony Chazapis
        self._check_groups(groups)
294 0f9d752c Antony Chazapis
        if replace:
295 0f9d752c Antony Chazapis
            self.permissions.group_destroy(account)
296 0f9d752c Antony Chazapis
        for k, v in groups.iteritems():
297 2715ade4 Sofia Papagiannaki
            if not replace:  # If not already deleted.
298 0f9d752c Antony Chazapis
                self.permissions.group_delete(account, k)
299 0f9d752c Antony Chazapis
            if v:
300 0f9d752c Antony Chazapis
                self.permissions.group_addmany(account, k, v)
301 2715ade4 Sofia Papagiannaki
302 a9b3f29d Antony Chazapis
    @backend_method
303 b2832c6a Antony Chazapis
    def get_account_policy(self, user, account):
304 b2832c6a Antony Chazapis
        """Return a dictionary with the account policy."""
305 2715ade4 Sofia Papagiannaki
306 fe2db49d Sofia Papagiannaki
        logger.debug("get_account_policy: %s %s", user, account)
307 b2832c6a Antony Chazapis
        if user != account:
308 647a5f48 Antony Chazapis
            if account not in self._allowed_accounts(user):
309 647a5f48 Antony Chazapis
                raise NotAllowedError
310 647a5f48 Antony Chazapis
            return {}
311 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
312 b9064632 Antony Chazapis
        return self._get_policy(node)
313 2715ade4 Sofia Papagiannaki
314 b2832c6a Antony Chazapis
    @backend_method
315 b2832c6a Antony Chazapis
    def update_account_policy(self, user, account, policy, replace=False):
316 b2832c6a Antony Chazapis
        """Update the policy associated with the account."""
317 2715ade4 Sofia Papagiannaki
318 2715ade4 Sofia Papagiannaki
        logger.debug("update_account_policy: %s %s %s %s", user,
319 2715ade4 Sofia Papagiannaki
                     account, policy, replace)
320 b2832c6a Antony Chazapis
        if user != account:
321 b2832c6a Antony Chazapis
            raise NotAllowedError
322 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
323 b2832c6a Antony Chazapis
        self._check_policy(policy)
324 b2832c6a Antony Chazapis
        self._put_policy(node, policy, replace)
325 2715ade4 Sofia Papagiannaki
326 b2832c6a Antony Chazapis
    @backend_method
327 8693b873 Sofia Papagiannaki
    def put_account(self, user, account, policy={}):
328 a9b3f29d Antony Chazapis
        """Create a new account with the given name."""
329 2715ade4 Sofia Papagiannaki
330 fe2db49d Sofia Papagiannaki
        logger.debug("put_account: %s %s %s", user, account, policy)
331 a9b3f29d Antony Chazapis
        if user != account:
332 a9b3f29d Antony Chazapis
            raise NotAllowedError
333 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
334 44ad5860 Antony Chazapis
        if node is not None:
335 7efc9f86 Sofia Papagiannaki
            raise AccountExists('Account already exists')
336 b2832c6a Antony Chazapis
        if policy:
337 b2832c6a Antony Chazapis
            self._check_policy(policy)
338 b2832c6a Antony Chazapis
        node = self._put_path(user, self.ROOTNODE, account)
339 b2832c6a Antony Chazapis
        self._put_policy(node, policy, True)
340 2715ade4 Sofia Papagiannaki
341 a9b3f29d Antony Chazapis
    @backend_method
342 a9b3f29d Antony Chazapis
    def delete_account(self, user, account):
343 a9b3f29d Antony Chazapis
        """Delete the account with the given name."""
344 2715ade4 Sofia Papagiannaki
345 fe2db49d Sofia Papagiannaki
        logger.debug("delete_account: %s %s", user, account)
346 a9b3f29d Antony Chazapis
        if user != account:
347 a9b3f29d Antony Chazapis
            raise NotAllowedError
348 c915d3bf Antony Chazapis
        node = self.node.node_lookup(account)
349 c915d3bf Antony Chazapis
        if node is None:
350 c915d3bf Antony Chazapis
            return
351 c915d3bf Antony Chazapis
        if not self.node.node_remove(node):
352 7efc9f86 Sofia Papagiannaki
            raise AccountNotEmpty('Account is not empty')
353 0f9d752c Antony Chazapis
        self.permissions.group_destroy(account)
354 2715ade4 Sofia Papagiannaki
355 62f915a1 Antony Chazapis
    @backend_method
356 90ee1eb3 Sofia Papagiannaki
    def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None, public=False):
357 62f915a1 Antony Chazapis
        """Return a list of containers existing under an account."""
358 2715ade4 Sofia Papagiannaki
359 2715ade4 Sofia Papagiannaki
        logger.debug("list_containers: %s %s %s %s %s %s %s", user,
360 2715ade4 Sofia Papagiannaki
                     account, marker, limit, shared, until, public)
361 62f915a1 Antony Chazapis
        if user != account:
362 62f915a1 Antony Chazapis
            if until or account not in self._allowed_accounts(user):
363 62f915a1 Antony Chazapis
                raise NotAllowedError
364 62f915a1 Antony Chazapis
            allowed = self._allowed_containers(user, account)
365 62f915a1 Antony Chazapis
            start, limit = self._list_limits(allowed, marker, limit)
366 62f915a1 Antony Chazapis
            return allowed[start:start + limit]
367 90ee1eb3 Sofia Papagiannaki
        if shared or public:
368 56ac7c81 Sofia Papagiannaki
            allowed = set()
369 90ee1eb3 Sofia Papagiannaki
            if shared:
370 56ac7c81 Sofia Papagiannaki
                allowed.update([x.split('/', 2)[1] for x in self.permissions.access_list_shared(account)])
371 90ee1eb3 Sofia Papagiannaki
            if public:
372 56ac7c81 Sofia Papagiannaki
                allowed.update([x[0].split('/', 2)[1] for x in self.permissions.public_list(account)])
373 56ac7c81 Sofia Papagiannaki
            allowed = sorted(allowed)
374 62f915a1 Antony Chazapis
            start, limit = self._list_limits(allowed, marker, limit)
375 62f915a1 Antony Chazapis
            return allowed[start:start + limit]
376 62f915a1 Antony Chazapis
        node = self.node.node_lookup(account)
377 2715ade4 Sofia Papagiannaki
        containers = [x[0] for x in self._list_object_properties(
378 2715ade4 Sofia Papagiannaki
            node, account, '', '/', marker, limit, False, None, [], until)]
379 2715ade4 Sofia Papagiannaki
        start, limit = self._list_limits(
380 2715ade4 Sofia Papagiannaki
            [x[0] for x in containers], marker, limit)
381 cf4a7a7b Sofia Papagiannaki
        return containers[start:start + limit]
382 2715ade4 Sofia Papagiannaki
383 371d907a Antony Chazapis
    @backend_method
384 371d907a Antony Chazapis
    def list_container_meta(self, user, account, container, domain, until=None):
385 371d907a Antony Chazapis
        """Return a list with all the container's object meta keys for the domain."""
386 2715ade4 Sofia Papagiannaki
387 2715ade4 Sofia Papagiannaki
        logger.debug("list_container_meta: %s %s %s %s %s", user,
388 2715ade4 Sofia Papagiannaki
                     account, container, domain, until)
389 371d907a Antony Chazapis
        allowed = []
390 371d907a Antony Chazapis
        if user != account:
391 371d907a Antony Chazapis
            if until:
392 371d907a Antony Chazapis
                raise NotAllowedError
393 2715ade4 Sofia Papagiannaki
            allowed = self.permissions.access_list_paths(
394 2715ade4 Sofia Papagiannaki
                user, '/'.join((account, container)))
395 371d907a Antony Chazapis
            if not allowed:
396 371d907a Antony Chazapis
                raise NotAllowedError
397 371d907a Antony Chazapis
        path, node = self._lookup_container(account, container)
398 371d907a Antony Chazapis
        before = until if until is not None else inf
399 371d907a Antony Chazapis
        allowed = self._get_formatted_paths(allowed)
400 371d907a Antony Chazapis
        return self.node.latest_attribute_keys(node, domain, before, CLUSTER_DELETED, allowed)
401 2715ade4 Sofia Papagiannaki
402 a9b3f29d Antony Chazapis
    @backend_method
403 82482e2c Antony Chazapis
    def get_container_meta(self, user, account, container, domain, until=None, include_user_defined=True):
404 cb69c154 Antony Chazapis
        """Return a dictionary with the container metadata for the domain."""
405 2715ade4 Sofia Papagiannaki
406 2715ade4 Sofia Papagiannaki
        logger.debug("get_container_meta: %s %s %s %s %s", user,
407 2715ade4 Sofia Papagiannaki
                     account, container, domain, until)
408 a9b3f29d Antony Chazapis
        if user != account:
409 a9b3f29d Antony Chazapis
            if until or container not in self._allowed_containers(user, account):
410 a9b3f29d Antony Chazapis
                raise NotAllowedError
411 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
412 c915d3bf Antony Chazapis
        props = self._get_properties(node, until)
413 2c5363a0 Antony Chazapis
        mtime = props[self.MTIME]
414 62f915a1 Antony Chazapis
        count, bytes, tstamp = self._get_statistics(node, until)
415 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
416 a9b3f29d Antony Chazapis
        if until is None:
417 a9b3f29d Antony Chazapis
            modified = tstamp
418 a9b3f29d Antony Chazapis
        else:
419 2715ade4 Sofia Papagiannaki
            modified = self._get_statistics(
420 2715ade4 Sofia Papagiannaki
                node)[2]  # Overall last modification.
421 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
422 2715ade4 Sofia Papagiannaki
423 a9b3f29d Antony Chazapis
        if user != account:
424 c915d3bf Antony Chazapis
            meta = {'name': container}
425 a9b3f29d Antony Chazapis
        else:
426 82482e2c Antony Chazapis
            meta = {}
427 82482e2c Antony Chazapis
            if include_user_defined:
428 2715ade4 Sofia Papagiannaki
                meta.update(
429 2715ade4 Sofia Papagiannaki
                    dict(self.node.attribute_get(props[self.SERIAL], domain)))
430 a9b3f29d Antony Chazapis
            if until is not None:
431 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
432 c915d3bf Antony Chazapis
            meta.update({'name': container, 'count': count, 'bytes': bytes})
433 c915d3bf Antony Chazapis
        meta.update({'modified': modified})
434 a9b3f29d Antony Chazapis
        return meta
435 2715ade4 Sofia Papagiannaki
436 a9b3f29d Antony Chazapis
    @backend_method
437 cb69c154 Antony Chazapis
    def update_container_meta(self, user, account, container, domain, meta, replace=False):
438 cb69c154 Antony Chazapis
        """Update the metadata associated with the container for the domain."""
439 2715ade4 Sofia Papagiannaki
440 2715ade4 Sofia Papagiannaki
        logger.debug("update_container_meta: %s %s %s %s %s %s",
441 2715ade4 Sofia Papagiannaki
                     user, account, container, domain, meta, replace)
442 a9b3f29d Antony Chazapis
        if user != account:
443 a9b3f29d Antony Chazapis
            raise NotAllowedError
444 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
445 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
446 2715ade4 Sofia Papagiannaki
            user, node, domain, meta, replace)
447 f9ea264b Antony Chazapis
        if src_version_id is not None:
448 f9ea264b Antony Chazapis
            versioning = self._get_policy(node)['versioning']
449 f9ea264b Antony Chazapis
            if versioning != 'auto':
450 f9ea264b Antony Chazapis
                self.node.version_remove(src_version_id)
451 2715ade4 Sofia Papagiannaki
452 a9b3f29d Antony Chazapis
    @backend_method
453 a9b3f29d Antony Chazapis
    def get_container_policy(self, user, account, container):
454 a9b3f29d Antony Chazapis
        """Return a dictionary with the container policy."""
455 2715ade4 Sofia Papagiannaki
456 2715ade4 Sofia Papagiannaki
        logger.debug(
457 2715ade4 Sofia Papagiannaki
            "get_container_policy: %s %s %s", user, account, container)
458 a9b3f29d Antony Chazapis
        if user != account:
459 a9b3f29d Antony Chazapis
            if container not in self._allowed_containers(user, account):
460 a9b3f29d Antony Chazapis
                raise NotAllowedError
461 a9b3f29d Antony Chazapis
            return {}
462 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
463 b9064632 Antony Chazapis
        return self._get_policy(node)
464 2715ade4 Sofia Papagiannaki
465 a9b3f29d Antony Chazapis
    @backend_method
466 a9b3f29d Antony Chazapis
    def update_container_policy(self, user, account, container, policy, replace=False):
467 b2832c6a Antony Chazapis
        """Update the policy associated with the container."""
468 2715ade4 Sofia Papagiannaki
469 2715ade4 Sofia Papagiannaki
        logger.debug("update_container_policy: %s %s %s %s %s",
470 2715ade4 Sofia Papagiannaki
                     user, account, container, policy, replace)
471 a9b3f29d Antony Chazapis
        if user != account:
472 a9b3f29d Antony Chazapis
            raise NotAllowedError
473 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
474 a9b3f29d Antony Chazapis
        self._check_policy(policy)
475 b2832c6a Antony Chazapis
        self._put_policy(node, policy, replace)
476 2715ade4 Sofia Papagiannaki
477 a9b3f29d Antony Chazapis
    @backend_method
478 8693b873 Sofia Papagiannaki
    def put_container(self, user, account, container, policy={}):
479 a9b3f29d Antony Chazapis
        """Create a new container with the given name."""
480 2715ade4 Sofia Papagiannaki
481 2715ade4 Sofia Papagiannaki
        logger.debug(
482 2715ade4 Sofia Papagiannaki
            "put_container: %s %s %s %s", user, account, container, policy)
483 a9b3f29d Antony Chazapis
        if user != account:
484 a9b3f29d Antony Chazapis
            raise NotAllowedError
485 a9b3f29d Antony Chazapis
        try:
486 c915d3bf Antony Chazapis
            path, node = self._lookup_container(account, container)
487 a9b3f29d Antony Chazapis
        except NameError:
488 a9b3f29d Antony Chazapis
            pass
489 a9b3f29d Antony Chazapis
        else:
490 7efc9f86 Sofia Papagiannaki
            raise ContainerExists('Container already exists')
491 a9b3f29d Antony Chazapis
        if policy:
492 a9b3f29d Antony Chazapis
            self._check_policy(policy)
493 a9b3f29d Antony Chazapis
        path = '/'.join((account, container))
494 2715ade4 Sofia Papagiannaki
        node = self._put_path(
495 2715ade4 Sofia Papagiannaki
            user, self._lookup_account(account, True)[1], path)
496 b2832c6a Antony Chazapis
        self._put_policy(node, policy, True)
497 2715ade4 Sofia Papagiannaki
498 a9b3f29d Antony Chazapis
    @backend_method
499 fe2db49d Sofia Papagiannaki
    def delete_container(self, user, account, container, until=None, prefix='', delimiter=None):
500 a9b3f29d Antony Chazapis
        """Delete/purge the container with the given name."""
501 2715ade4 Sofia Papagiannaki
502 2715ade4 Sofia Papagiannaki
        logger.debug("delete_container: %s %s %s %s %s %s", user,
503 2715ade4 Sofia Papagiannaki
                     account, container, until, prefix, delimiter)
504 a9b3f29d Antony Chazapis
        if user != account:
505 a9b3f29d Antony Chazapis
            raise NotAllowedError
506 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
507 2715ade4 Sofia Papagiannaki
508 a9b3f29d Antony Chazapis
        if until is not None:
509 388ea25f Sofia Papagiannaki
            hashes, size, serials = self.node.node_purge_children(
510 2715ade4 Sofia Papagiannaki
                node, until, CLUSTER_HISTORY)
511 04230536 Antony Chazapis
            for h in hashes:
512 04230536 Antony Chazapis
                self.store.map_delete(h)
513 c915d3bf Antony Chazapis
            self.node.node_purge_children(node, until, CLUSTER_DELETED)
514 388ea25f Sofia Papagiannaki
            self._report_size_change(user, account, -size,
515 7ed99da8 root
                                     {'action':'container purge', 'path': path,
516 b1dadd0e Sofia Papagiannaki
                                      'versions': ','.join(str(i) for i in serials)})
517 a9b3f29d Antony Chazapis
            return
518 2715ade4 Sofia Papagiannaki
519 e46b2bcf Sofia Papagiannaki
        if not delimiter:
520 e46b2bcf Sofia Papagiannaki
            if self._get_statistics(node)[0] > 0:
521 e46b2bcf Sofia Papagiannaki
                raise ContainerNotEmpty('Container is not empty')
522 388ea25f Sofia Papagiannaki
            hashes, size, serials = self.node.node_purge_children(
523 2715ade4 Sofia Papagiannaki
                node, inf, CLUSTER_HISTORY)
524 e46b2bcf Sofia Papagiannaki
            for h in hashes:
525 e46b2bcf Sofia Papagiannaki
                self.store.map_delete(h)
526 e46b2bcf Sofia Papagiannaki
            self.node.node_purge_children(node, inf, CLUSTER_DELETED)
527 e46b2bcf Sofia Papagiannaki
            self.node.node_remove(node)
528 388ea25f Sofia Papagiannaki
            self._report_size_change(user, account, -size,
529 7ed99da8 root
                                     {'action': 'container delete',
530 7ed99da8 root
                                      'path': path,
531 b1dadd0e Sofia Papagiannaki
                                      'versions': ','.join(str(i) for i in serials)})
532 e46b2bcf Sofia Papagiannaki
        else:
533 b1dadd0e Sofia Papagiannaki
            # remove only contents
534 e46b2bcf Sofia Papagiannaki
            src_names = self._list_objects_no_limit(user, account, container, prefix='', delimiter=None, virtual=False, domain=None, keys=[], shared=False, until=None, size_range=None, all_props=True, public=False)
535 e46b2bcf Sofia Papagiannaki
            paths = []
536 e46b2bcf Sofia Papagiannaki
            for t in src_names:
537 e46b2bcf Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
538 e46b2bcf Sofia Papagiannaki
                node = t[2]
539 e46b2bcf Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(user, node, size=0, type='', hash=None, checksum='', cluster=CLUSTER_DELETED)
540 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
541 2715ade4 Sofia Papagiannaki
                    account, container, src_version_id)
542 b1dadd0e Sofia Papagiannaki
                self._report_size_change(
543 b1dadd0e Sofia Papagiannaki
                        user, account, -del_size, {
544 b1dadd0e Sofia Papagiannaki
                                'action': 'object delete',
545 b1dadd0e Sofia Papagiannaki
                                'path': path,
546 b1dadd0e Sofia Papagiannaki
                             'versions': ','.join([str(dest_version_id)])
547 b1dadd0e Sofia Papagiannaki
                     }
548 b1dadd0e Sofia Papagiannaki
                )
549 2715ade4 Sofia Papagiannaki
                self._report_object_change(
550 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
551 e46b2bcf Sofia Papagiannaki
                paths.append(path)
552 e46b2bcf Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
553 2715ade4 Sofia Papagiannaki
554 90ee1eb3 Sofia Papagiannaki
    def _list_objects(self, user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, all_props, public):
555 15a96c3e Antony Chazapis
        if user != account and until:
556 15a96c3e Antony Chazapis
            raise NotAllowedError
557 cf4a7a7b Sofia Papagiannaki
        if shared and public:
558 cf4a7a7b Sofia Papagiannaki
            # get shared first
559 2715ade4 Sofia Papagiannaki
            shared = self._list_object_permissions(
560 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, shared=True, public=False)
561 31e1acd3 Sofia Papagiannaki
            objects = set()
562 cf4a7a7b Sofia Papagiannaki
            if shared:
563 cf4a7a7b Sofia Papagiannaki
                path, node = self._lookup_container(account, container)
564 cf4a7a7b Sofia Papagiannaki
                shared = self._get_formatted_paths(shared)
565 31e1acd3 Sofia Papagiannaki
                objects |= set(self._list_object_properties(node, path, prefix, delimiter, marker, limit, virtual, domain, keys, until, size_range, shared, all_props))
566 2715ade4 Sofia Papagiannaki
567 cf4a7a7b Sofia Papagiannaki
            # get public
568 2715ade4 Sofia Papagiannaki
            objects |= set(self._list_public_object_properties(
569 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, all_props))
570 31e1acd3 Sofia Papagiannaki
            objects = list(objects)
571 2715ade4 Sofia Papagiannaki
572 cf4a7a7b Sofia Papagiannaki
            objects.sort(key=lambda x: x[0])
573 2715ade4 Sofia Papagiannaki
            start, limit = self._list_limits(
574 2715ade4 Sofia Papagiannaki
                [x[0] for x in objects], marker, limit)
575 cf4a7a7b Sofia Papagiannaki
            return objects[start:start + limit]
576 cf4a7a7b Sofia Papagiannaki
        elif public:
577 2715ade4 Sofia Papagiannaki
            objects = self._list_public_object_properties(
578 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, all_props)
579 2715ade4 Sofia Papagiannaki
            start, limit = self._list_limits(
580 2715ade4 Sofia Papagiannaki
                [x[0] for x in objects], marker, limit)
581 cf4a7a7b Sofia Papagiannaki
            return objects[start:start + limit]
582 2715ade4 Sofia Papagiannaki
583 2715ade4 Sofia Papagiannaki
        allowed = self._list_object_permissions(
584 2715ade4 Sofia Papagiannaki
            user, account, container, prefix, shared, public)
585 cf4a7a7b Sofia Papagiannaki
        if shared and not allowed:
586 fcd37c40 Antony Chazapis
            return []
587 15a96c3e Antony Chazapis
        path, node = self._lookup_container(account, container)
588 15a96c3e Antony Chazapis
        allowed = self._get_formatted_paths(allowed)
589 cf4a7a7b Sofia Papagiannaki
        objects = self._list_object_properties(node, path, prefix, delimiter, marker, limit, virtual, domain, keys, until, size_range, allowed, all_props)
590 2715ade4 Sofia Papagiannaki
        start, limit = self._list_limits(
591 2715ade4 Sofia Papagiannaki
            [x[0] for x in objects], marker, limit)
592 cf4a7a7b Sofia Papagiannaki
        return objects[start:start + limit]
593 2715ade4 Sofia Papagiannaki
594 cf4a7a7b Sofia Papagiannaki
    def _list_public_object_properties(self, user, account, container, prefix, all_props):
595 2715ade4 Sofia Papagiannaki
        public = self._list_object_permissions(
596 2715ade4 Sofia Papagiannaki
            user, account, container, prefix, shared=False, public=True)
597 cf4a7a7b Sofia Papagiannaki
        paths, nodes = self._lookup_objects(public)
598 cf4a7a7b Sofia Papagiannaki
        path = '/'.join((account, container))
599 cf4a7a7b Sofia Papagiannaki
        cont_prefix = path + '/'
600 cf4a7a7b Sofia Papagiannaki
        paths = [x[len(cont_prefix):] for x in paths]
601 cf4a7a7b Sofia Papagiannaki
        props = self.node.version_lookup_bulk(nodes, all_props=all_props)
602 cf4a7a7b Sofia Papagiannaki
        objects = [(path,) + props for path, props in zip(paths, props)]
603 cf4a7a7b Sofia Papagiannaki
        return objects
604 2715ade4 Sofia Papagiannaki
605 4d15c94e Sofia Papagiannaki
    def _list_objects_no_limit(self, user, account, container, prefix, delimiter, virtual, domain, keys, shared, until, size_range, all_props, public):
606 4d15c94e Sofia Papagiannaki
        objects = []
607 4d15c94e Sofia Papagiannaki
        while True:
608 4d15c94e Sofia Papagiannaki
            marker = objects[-1] if objects else None
609 4d15c94e Sofia Papagiannaki
            limit = 10000
610 4d15c94e Sofia Papagiannaki
            l = self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, all_props, public)
611 4d15c94e Sofia Papagiannaki
            objects.extend(l)
612 4d15c94e Sofia Papagiannaki
            if not l or len(l) < limit:
613 4d15c94e Sofia Papagiannaki
                break
614 4d15c94e Sofia Papagiannaki
        return objects
615 2715ade4 Sofia Papagiannaki
616 90ee1eb3 Sofia Papagiannaki
    def _list_object_permissions(self, user, account, container, prefix, shared, public):
617 62f915a1 Antony Chazapis
        allowed = []
618 fcd37c40 Antony Chazapis
        path = '/'.join((account, container, prefix)).rstrip('/')
619 62f915a1 Antony Chazapis
        if user != account:
620 fcd37c40 Antony Chazapis
            allowed = self.permissions.access_list_paths(user, path)
621 62f915a1 Antony Chazapis
            if not allowed:
622 62f915a1 Antony Chazapis
                raise NotAllowedError
623 62f915a1 Antony Chazapis
        else:
624 56ac7c81 Sofia Papagiannaki
            allowed = set()
625 62f915a1 Antony Chazapis
            if shared:
626 56ac7c81 Sofia Papagiannaki
                allowed.update(self.permissions.access_list_shared(path))
627 c53c4def Sofia Papagiannaki
            if public:
628 2715ade4 Sofia Papagiannaki
                allowed.update(
629 2715ade4 Sofia Papagiannaki
                    [x[0] for x in self.permissions.public_list(path)])
630 56ac7c81 Sofia Papagiannaki
            allowed = sorted(allowed)
631 c53c4def Sofia Papagiannaki
            if not allowed:
632 c53c4def Sofia Papagiannaki
                return []
633 15a96c3e Antony Chazapis
        return allowed
634 2715ade4 Sofia Papagiannaki
635 62f915a1 Antony Chazapis
    @backend_method
636 90ee1eb3 Sofia Papagiannaki
    def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None, size_range=None, public=False):
637 371d907a Antony Chazapis
        """Return a list of object (name, version_id) tuples existing under a container."""
638 2715ade4 Sofia Papagiannaki
639 fe2db49d Sofia Papagiannaki
        logger.debug("list_objects: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, public)
640 90ee1eb3 Sofia Papagiannaki
        return self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, False, public)
641 2715ade4 Sofia Papagiannaki
642 371d907a Antony Chazapis
    @backend_method
643 90ee1eb3 Sofia Papagiannaki
    def list_object_meta(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None, size_range=None, public=False):
644 371d907a Antony Chazapis
        """Return a list of object metadata dicts existing under a container."""
645 2715ade4 Sofia Papagiannaki
646 fe2db49d Sofia Papagiannaki
        logger.debug("list_object_meta: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, public)
647 90ee1eb3 Sofia Papagiannaki
        props = self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, True, public)
648 371d907a Antony Chazapis
        objects = []
649 371d907a Antony Chazapis
        for p in props:
650 371d907a Antony Chazapis
            if len(p) == 2:
651 371d907a Antony Chazapis
                objects.append({'subdir': p[0]})
652 371d907a Antony Chazapis
            else:
653 371d907a Antony Chazapis
                objects.append({'name': p[0],
654 371d907a Antony Chazapis
                                'bytes': p[self.SIZE + 1],
655 371d907a Antony Chazapis
                                'type': p[self.TYPE + 1],
656 371d907a Antony Chazapis
                                'hash': p[self.HASH + 1],
657 371d907a Antony Chazapis
                                'version': p[self.SERIAL + 1],
658 371d907a Antony Chazapis
                                'version_timestamp': p[self.MTIME + 1],
659 371d907a Antony Chazapis
                                'modified': p[self.MTIME + 1] if until is None else None,
660 371d907a Antony Chazapis
                                'modified_by': p[self.MUSER + 1],
661 371d907a Antony Chazapis
                                'uuid': p[self.UUID + 1],
662 371d907a Antony Chazapis
                                'checksum': p[self.CHECKSUM + 1]})
663 371d907a Antony Chazapis
        return objects
664 2715ade4 Sofia Papagiannaki
665 a9b3f29d Antony Chazapis
    @backend_method
666 15a96c3e Antony Chazapis
    def list_object_permissions(self, user, account, container, prefix=''):
667 15a96c3e Antony Chazapis
        """Return a list of paths that enforce permissions under a container."""
668 2715ade4 Sofia Papagiannaki
669 2715ade4 Sofia Papagiannaki
        logger.debug("list_object_permissions: %s %s %s %s", user,
670 2715ade4 Sofia Papagiannaki
                     account, container, prefix)
671 90ee1eb3 Sofia Papagiannaki
        return self._list_object_permissions(user, account, container, prefix, True, False)
672 2715ade4 Sofia Papagiannaki
673 15a96c3e Antony Chazapis
    @backend_method
674 15a96c3e Antony Chazapis
    def list_object_public(self, user, account, container, prefix=''):
675 15a96c3e Antony Chazapis
        """Return a dict mapping paths to public ids for objects that are public under a container."""
676 2715ade4 Sofia Papagiannaki
677 2715ade4 Sofia Papagiannaki
        logger.debug("list_object_public: %s %s %s %s", user,
678 2715ade4 Sofia Papagiannaki
                     account, container, prefix)
679 15a96c3e Antony Chazapis
        public = {}
680 15a96c3e Antony Chazapis
        for path, p in self.permissions.public_list('/'.join((account, container, prefix))):
681 15a96c3e Antony Chazapis
            public[path] = p + ULTIMATE_ANSWER
682 15a96c3e Antony Chazapis
        return public
683 2715ade4 Sofia Papagiannaki
684 15a96c3e Antony Chazapis
    @backend_method
685 82482e2c Antony Chazapis
    def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
686 cb69c154 Antony Chazapis
        """Return a dictionary with the object metadata for the domain."""
687 2715ade4 Sofia Papagiannaki
688 2715ade4 Sofia Papagiannaki
        logger.debug("get_object_meta: %s %s %s %s %s %s", user,
689 2715ade4 Sofia Papagiannaki
                     account, container, name, domain, version)
690 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
691 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
692 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
693 a9b3f29d Antony Chazapis
        if version is None:
694 2c5363a0 Antony Chazapis
            modified = props[self.MTIME]
695 a9b3f29d Antony Chazapis
        else:
696 97d45f69 Antony Chazapis
            try:
697 2715ade4 Sofia Papagiannaki
                modified = self._get_version(
698 2715ade4 Sofia Papagiannaki
                    node)[self.MTIME]  # Overall last modification.
699 2715ade4 Sofia Papagiannaki
            except NameError:  # Object may be deleted.
700 2715ade4 Sofia Papagiannaki
                del_props = self.node.version_lookup(
701 2715ade4 Sofia Papagiannaki
                    node, inf, CLUSTER_DELETED)
702 97d45f69 Antony Chazapis
                if del_props is None:
703 7efc9f86 Sofia Papagiannaki
                    raise ItemNotExists('Object does not exist')
704 97d45f69 Antony Chazapis
                modified = del_props[self.MTIME]
705 2715ade4 Sofia Papagiannaki
706 82482e2c Antony Chazapis
        meta = {}
707 82482e2c Antony Chazapis
        if include_user_defined:
708 2715ade4 Sofia Papagiannaki
            meta.update(
709 2715ade4 Sofia Papagiannaki
                dict(self.node.attribute_get(props[self.SERIAL], domain)))
710 33b4e4a6 Antony Chazapis
        meta.update({'name': name,
711 33b4e4a6 Antony Chazapis
                     'bytes': props[self.SIZE],
712 33b4e4a6 Antony Chazapis
                     'type': props[self.TYPE],
713 371d907a Antony Chazapis
                     'hash': props[self.HASH],
714 33b4e4a6 Antony Chazapis
                     'version': props[self.SERIAL],
715 33b4e4a6 Antony Chazapis
                     'version_timestamp': props[self.MTIME],
716 33b4e4a6 Antony Chazapis
                     'modified': modified,
717 33b4e4a6 Antony Chazapis
                     'modified_by': props[self.MUSER],
718 33b4e4a6 Antony Chazapis
                     'uuid': props[self.UUID],
719 33b4e4a6 Antony Chazapis
                     'checksum': props[self.CHECKSUM]})
720 a9b3f29d Antony Chazapis
        return meta
721 2715ade4 Sofia Papagiannaki
722 a9b3f29d Antony Chazapis
    @backend_method
723 cb69c154 Antony Chazapis
    def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
724 cb69c154 Antony Chazapis
        """Update the metadata associated with the object for the domain and return the new version."""
725 2715ade4 Sofia Papagiannaki
726 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_meta: %s %s %s %s %s %s %s",
727 2715ade4 Sofia Papagiannaki
                     user, account, container, name, domain, meta, replace)
728 a9b3f29d Antony Chazapis
        self._can_write(user, account, container, name)
729 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
730 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
731 2715ade4 Sofia Papagiannaki
            user, node, domain, meta, replace)
732 5cc484e1 Antony Chazapis
        self._apply_versioning(account, container, src_version_id)
733 5cc484e1 Antony Chazapis
        return dest_version_id
734 2715ade4 Sofia Papagiannaki
735 a9b3f29d Antony Chazapis
    @backend_method
736 a9b3f29d Antony Chazapis
    def get_object_permissions(self, user, account, container, name):
737 067cf1fc Antony Chazapis
        """Return the action allowed on the object, the path
738 067cf1fc Antony Chazapis
        from which the object gets its permissions from,
739 a9b3f29d Antony Chazapis
        along with a dictionary containing the permissions."""
740 2715ade4 Sofia Papagiannaki
741 2715ade4 Sofia Papagiannaki
        logger.debug("get_object_permissions: %s %s %s %s", user,
742 2715ade4 Sofia Papagiannaki
                     account, container, name)
743 067cf1fc Antony Chazapis
        allowed = 'write'
744 92da0e5a Antony Chazapis
        permissions_path = self._get_permissions_path(account, container, name)
745 067cf1fc Antony Chazapis
        if user != account:
746 92da0e5a Antony Chazapis
            if self.permissions.access_check(permissions_path, self.WRITE, user):
747 067cf1fc Antony Chazapis
                allowed = 'write'
748 92da0e5a Antony Chazapis
            elif self.permissions.access_check(permissions_path, self.READ, user):
749 067cf1fc Antony Chazapis
                allowed = 'read'
750 067cf1fc Antony Chazapis
            else:
751 067cf1fc Antony Chazapis
                raise NotAllowedError
752 92da0e5a Antony Chazapis
        self._lookup_object(account, container, name)
753 92da0e5a Antony Chazapis
        return (allowed, permissions_path, self.permissions.access_get(permissions_path))
754 2715ade4 Sofia Papagiannaki
755 a9b3f29d Antony Chazapis
    @backend_method
756 a9b3f29d Antony Chazapis
    def update_object_permissions(self, user, account, container, name, permissions):
757 a9b3f29d Antony Chazapis
        """Update the permissions associated with the object."""
758 2715ade4 Sofia Papagiannaki
759 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_permissions: %s %s %s %s %s",
760 2715ade4 Sofia Papagiannaki
                     user, account, container, name, permissions)
761 a9b3f29d Antony Chazapis
        if user != account:
762 a9b3f29d Antony Chazapis
            raise NotAllowedError
763 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
764 6f4bce7b Antony Chazapis
        self._check_permissions(path, permissions)
765 0f9d752c Antony Chazapis
        self.permissions.access_set(path, permissions)
766 2715ade4 Sofia Papagiannaki
        self._report_sharing_change(user, account, path, {'members':
767 2715ade4 Sofia Papagiannaki
                                    self.permissions.access_members(path)})
768 2715ade4 Sofia Papagiannaki
769 a9b3f29d Antony Chazapis
    @backend_method
770 a9b3f29d Antony Chazapis
    def get_object_public(self, user, account, container, name):
771 bb4eafc6 Antony Chazapis
        """Return the public id of the object if applicable."""
772 2715ade4 Sofia Papagiannaki
773 2715ade4 Sofia Papagiannaki
        logger.debug(
774 2715ade4 Sofia Papagiannaki
            "get_object_public: %s %s %s %s", user, account, container, name)
775 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
776 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
777 bb4eafc6 Antony Chazapis
        p = self.permissions.public_get(path)
778 bb4eafc6 Antony Chazapis
        if p is not None:
779 bb4eafc6 Antony Chazapis
            p += ULTIMATE_ANSWER
780 bb4eafc6 Antony Chazapis
        return p
781 2715ade4 Sofia Papagiannaki
782 a9b3f29d Antony Chazapis
    @backend_method
783 a9b3f29d Antony Chazapis
    def update_object_public(self, user, account, container, name, public):
784 a9b3f29d Antony Chazapis
        """Update the public status of the object."""
785 2715ade4 Sofia Papagiannaki
786 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_public: %s %s %s %s %s", user,
787 2715ade4 Sofia Papagiannaki
                     account, container, name, public)
788 a9b3f29d Antony Chazapis
        self._can_write(user, account, container, name)
789 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
790 0f9d752c Antony Chazapis
        if not public:
791 0f9d752c Antony Chazapis
            self.permissions.public_unset(path)
792 0f9d752c Antony Chazapis
        else:
793 0f9d752c Antony Chazapis
            self.permissions.public_set(path)
794 2715ade4 Sofia Papagiannaki
795 a9b3f29d Antony Chazapis
    @backend_method
796 a9b3f29d Antony Chazapis
    def get_object_hashmap(self, user, account, container, name, version=None):
797 a9b3f29d Antony Chazapis
        """Return the object's size and a list with partial hashes."""
798 2715ade4 Sofia Papagiannaki
799 2715ade4 Sofia Papagiannaki
        logger.debug("get_object_hashmap: %s %s %s %s %s", user,
800 2715ade4 Sofia Papagiannaki
                     account, container, name, version)
801 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
802 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
803 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
804 7ca7bb08 Antony Chazapis
        hashmap = self.store.map_get(binascii.unhexlify(props[self.HASH]))
805 2c5363a0 Antony Chazapis
        return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
806 2715ade4 Sofia Papagiannaki
807 f9ea264b Antony Chazapis
    def _update_object_hash(self, user, account, container, name, size, type, hash, checksum, domain, meta, replace_meta, permissions, src_node=None, src_version_id=None, is_copy=False):
808 b9064632 Antony Chazapis
        if permissions is not None and user != account:
809 b9064632 Antony Chazapis
            raise NotAllowedError
810 b9064632 Antony Chazapis
        self._can_write(user, account, container, name)
811 b9064632 Antony Chazapis
        if permissions is not None:
812 b9064632 Antony Chazapis
            path = '/'.join((account, container, name))
813 b9064632 Antony Chazapis
            self._check_permissions(path, permissions)
814 2715ade4 Sofia Papagiannaki
815 b9064632 Antony Chazapis
        account_path, account_node = self._lookup_account(account, True)
816 2715ade4 Sofia Papagiannaki
        container_path, container_node = self._lookup_container(
817 2715ade4 Sofia Papagiannaki
            account, container)
818 2715ade4 Sofia Papagiannaki
        path, node = self._put_object_node(
819 2715ade4 Sofia Papagiannaki
            container_path, container_node, name)
820 33b4e4a6 Antony Chazapis
        pre_version_id, dest_version_id = self._put_version_duplicate(user, node, src_node=src_node, size=size, type=type, hash=hash, checksum=checksum, is_copy=is_copy)
821 2715ade4 Sofia Papagiannaki
822 f9ea264b Antony Chazapis
        # Handle meta.
823 f9ea264b Antony Chazapis
        if src_version_id is None:
824 f9ea264b Antony Chazapis
            src_version_id = pre_version_id
825 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
826 2715ade4 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, meta, replace_meta)
827 2715ade4 Sofia Papagiannaki
828 813e42e5 Antony Chazapis
        del_size = self._apply_versioning(account, container, pre_version_id)
829 813e42e5 Antony Chazapis
        size_delta = size - del_size
830 d3655326 Sofia Papagiannaki
        if not self.quotaholder_url: # Check quota.
831 d3655326 Sofia Papagiannaki
            if size_delta > 0:
832 d3655326 Sofia Papagiannaki
                account_quota = long(self._get_policy(account_node)['quota'])
833 d3655326 Sofia Papagiannaki
                account_usage = self._get_statistics(account_node)[1] + size_delta
834 d3655326 Sofia Papagiannaki
                container_quota = long(self._get_policy(container_node)['quota'])
835 d3655326 Sofia Papagiannaki
                container_usage = self._get_statistics(container_node)[1] + size_delta
836 d3655326 Sofia Papagiannaki
                if (account_quota > 0 and account_usage > account_quota):
837 d3655326 Sofia Papagiannaki
                    logger.error('account_quota: %s, account_usage: %s' % (
838 d3655326 Sofia Papagiannaki
                        account_quota, account_usage
839 d3655326 Sofia Papagiannaki
                    ))
840 d3655326 Sofia Papagiannaki
                    raise QuotaError
841 d3655326 Sofia Papagiannaki
                if (container_quota > 0 and container_usage > container_quota):
842 d3655326 Sofia Papagiannaki
                    # This must be executed in a transaction, so the version is
843 d3655326 Sofia Papagiannaki
                    # never created if it fails.
844 d3655326 Sofia Papagiannaki
                    logger.error('container_quota: %s, container_usage: %s' % (
845 d3655326 Sofia Papagiannaki
                        container_quota, container_usage
846 d3655326 Sofia Papagiannaki
                    ))
847 d3655326 Sofia Papagiannaki
                    raise QuotaError
848 388ea25f Sofia Papagiannaki
        self._report_size_change(user, account, size_delta,
849 7ed99da8 root
                                 {'action': 'object update', 'path': path,
850 b1dadd0e Sofia Papagiannaki
                                  'versions': ','.join([str(dest_version_id)])})
851 b9064632 Antony Chazapis
        if permissions is not None:
852 b9064632 Antony Chazapis
            self.permissions.access_set(path, permissions)
853 2715ade4 Sofia Papagiannaki
            self._report_sharing_change(user, account, path, {'members': self.permissions.access_members(path)})
854 2715ade4 Sofia Papagiannaki
855 39ef6f41 Antony Chazapis
        self._report_object_change(user, account, path, details={'version': dest_version_id, 'action': 'object update'})
856 f9ea264b Antony Chazapis
        return dest_version_id
857 2715ade4 Sofia Papagiannaki
858 a9b3f29d Antony Chazapis
    @backend_method
859 33b4e4a6 Antony Chazapis
    def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta={}, replace_meta=False, permissions=None):
860 a9b3f29d Antony Chazapis
        """Create/update an object with the specified size and partial hashes."""
861 2715ade4 Sofia Papagiannaki
862 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_hashmap: %s %s %s %s %s %s %s %s", user,
863 2715ade4 Sofia Papagiannaki
                     account, container, name, size, type, hashmap, checksum)
864 2715ade4 Sofia Papagiannaki
        if size == 0:  # No such thing as an empty hashmap.
865 6d64339e Antony Chazapis
            hashmap = [self.put_block('')]
866 1c2fc0ff Antony Chazapis
        map = HashMap(self.block_size, self.hash_algorithm)
867 1c2fc0ff Antony Chazapis
        map.extend([binascii.unhexlify(x) for x in hashmap])
868 7ca7bb08 Antony Chazapis
        missing = self.store.block_search(map)
869 a9b3f29d Antony Chazapis
        if missing:
870 a9b3f29d Antony Chazapis
            ie = IndexError()
871 dd71f493 Antony Chazapis
            ie.data = [binascii.hexlify(x) for x in missing]
872 a9b3f29d Antony Chazapis
            raise ie
873 2715ade4 Sofia Papagiannaki
874 1c2fc0ff Antony Chazapis
        hash = map.hash()
875 f9ea264b Antony Chazapis
        dest_version_id = self._update_object_hash(user, account, container, name, size, type, binascii.hexlify(hash), checksum, domain, meta, replace_meta, permissions)
876 7ca7bb08 Antony Chazapis
        self.store.map_put(hash, map)
877 02c4d2ba Antony Chazapis
        return dest_version_id
878 2715ade4 Sofia Papagiannaki
879 33b4e4a6 Antony Chazapis
    @backend_method
880 33b4e4a6 Antony Chazapis
    def update_object_checksum(self, user, account, container, name, version, checksum):
881 33b4e4a6 Antony Chazapis
        """Update an object's checksum."""
882 2715ade4 Sofia Papagiannaki
883 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_checksum: %s %s %s %s %s %s",
884 2715ade4 Sofia Papagiannaki
                     user, account, container, name, version, checksum)
885 33b4e4a6 Antony Chazapis
        # Update objects with greater version and same hashmap and size (fix metadata updates).
886 33b4e4a6 Antony Chazapis
        self._can_write(user, account, container, name)
887 33b4e4a6 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
888 33b4e4a6 Antony Chazapis
        props = self._get_version(node, version)
889 33b4e4a6 Antony Chazapis
        versions = self.node.node_get_versions(node)
890 33b4e4a6 Antony Chazapis
        for x in versions:
891 33b4e4a6 Antony Chazapis
            if x[self.SERIAL] >= int(version) and x[self.HASH] == props[self.HASH] and x[self.SIZE] == props[self.SIZE]:
892 2715ade4 Sofia Papagiannaki
                self.node.version_put_property(
893 2715ade4 Sofia Papagiannaki
                    x[self.SERIAL], 'checksum', checksum)
894 2715ade4 Sofia Papagiannaki
895 4d15c94e Sofia Papagiannaki
    def _copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, dest_domain=None, dest_meta={}, replace_meta=False, permissions=None, src_version=None, is_move=False, delimiter=None):
896 4d15c94e Sofia Papagiannaki
        dest_version_ids = []
897 79bb41b7 Antony Chazapis
        self._can_read(user, src_account, src_container, src_name)
898 b9064632 Antony Chazapis
        path, node = self._lookup_object(src_account, src_container, src_name)
899 1730b3bf chazapis
        # TODO: Will do another fetch of the properties in duplicate version...
900 2715ade4 Sofia Papagiannaki
        props = self._get_version(
901 2715ade4 Sofia Papagiannaki
            node, src_version)  # Check to see if source exists.
902 b9064632 Antony Chazapis
        src_version_id = props[self.SERIAL]
903 b9064632 Antony Chazapis
        hash = props[self.HASH]
904 b9064632 Antony Chazapis
        size = props[self.SIZE]
905 2715ade4 Sofia Papagiannaki
        is_copy = not is_move and (src_account, src_container, src_name) != (
906 2715ade4 Sofia Papagiannaki
            dest_account, dest_container, dest_name)  # New uuid.
907 4d15c94e Sofia Papagiannaki
        dest_version_ids.append(self._update_object_hash(user, dest_account, dest_container, dest_name, size, type, hash, None, dest_domain, dest_meta, replace_meta, permissions, src_node=node, src_version_id=src_version_id, is_copy=is_copy))
908 3f767854 Sofia Papagiannaki
        if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
909 2715ade4 Sofia Papagiannaki
            self._delete_object(user, src_account, src_container, src_name)
910 2715ade4 Sofia Papagiannaki
911 4d15c94e Sofia Papagiannaki
        if delimiter:
912 2715ade4 Sofia Papagiannaki
            prefix = src_name + \
913 2715ade4 Sofia Papagiannaki
                delimiter if not src_name.endswith(delimiter) else src_name
914 e46b2bcf Sofia Papagiannaki
            src_names = self._list_objects_no_limit(user, src_account, src_container, prefix, delimiter=None, virtual=False, domain=None, keys=[], shared=False, until=None, size_range=None, all_props=True, public=False)
915 2715ade4 Sofia Papagiannaki
            src_names.sort(key=lambda x: x[2])  # order by nodes
916 4d15c94e Sofia Papagiannaki
            paths = [elem[0] for elem in src_names]
917 4d15c94e Sofia Papagiannaki
            nodes = [elem[2] for elem in src_names]
918 4d15c94e Sofia Papagiannaki
            # TODO: Will do another fetch of the properties in duplicate version...
919 2715ade4 Sofia Papagiannaki
            props = self._get_versions(nodes)  # Check to see if source exists.
920 2715ade4 Sofia Papagiannaki
921 4d15c94e Sofia Papagiannaki
            for prop, path, node in zip(props, paths, nodes):
922 4d15c94e Sofia Papagiannaki
                src_version_id = prop[self.SERIAL]
923 4d15c94e Sofia Papagiannaki
                hash = prop[self.HASH]
924 4d15c94e Sofia Papagiannaki
                vtype = prop[self.TYPE]
925 07867f70 Sofia Papagiannaki
                size = prop[self.SIZE]
926 2715ade4 Sofia Papagiannaki
                dest_prefix = dest_name + delimiter if not dest_name.endswith(
927 2715ade4 Sofia Papagiannaki
                    delimiter) else dest_name
928 4d15c94e Sofia Papagiannaki
                vdest_name = path.replace(prefix, dest_prefix, 1)
929 e46b2bcf Sofia Papagiannaki
                dest_version_ids.append(self._update_object_hash(user, dest_account, dest_container, vdest_name, size, vtype, hash, None, dest_domain, meta={}, replace_meta=False, permissions=None, src_node=node, src_version_id=src_version_id, is_copy=is_copy))
930 3f767854 Sofia Papagiannaki
                if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
931 2715ade4 Sofia Papagiannaki
                    self._delete_object(user, src_account, src_container, path)
932 4d15c94e Sofia Papagiannaki
        return dest_version_ids[0] if len(dest_version_ids) == 1 else dest_version_ids
933 2715ade4 Sofia Papagiannaki
934 4d15c94e Sofia Papagiannaki
    @backend_method
935 4d15c94e Sofia Papagiannaki
    def copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta={}, replace_meta=False, permissions=None, src_version=None, delimiter=None):
936 dff7b6f1 Sofia Papagiannaki
        """Copy an object's data and metadata."""
937 2715ade4 Sofia Papagiannaki
938 4d15c94e Sofia Papagiannaki
        logger.debug("copy_object: %s %s %s %s %s %s %s %s %s %s %s %s %s %s", user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, src_version, delimiter)
939 4d15c94e Sofia Papagiannaki
        dest_version_id = self._copy_object(user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, src_version, False, delimiter)
940 46286f5f Antony Chazapis
        return dest_version_id
941 2715ade4 Sofia Papagiannaki
942 dff7b6f1 Sofia Papagiannaki
    @backend_method
943 4d15c94e Sofia Papagiannaki
    def move_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta={}, replace_meta=False, permissions=None, delimiter=None):
944 a9b3f29d Antony Chazapis
        """Move an object's data and metadata."""
945 2715ade4 Sofia Papagiannaki
946 4d15c94e Sofia Papagiannaki
        logger.debug("move_object: %s %s %s %s %s %s %s %s %s %s %s %s %s", user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, delimiter)
947 79bb41b7 Antony Chazapis
        if user != src_account:
948 79bb41b7 Antony Chazapis
            raise NotAllowedError
949 4d15c94e Sofia Papagiannaki
        dest_version_id = self._copy_object(user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta, replace_meta, permissions, None, True, delimiter)
950 02c4d2ba Antony Chazapis
        return dest_version_id
951 2715ade4 Sofia Papagiannaki
952 4d15c94e Sofia Papagiannaki
    def _delete_object(self, user, account, container, name, until=None, delimiter=None):
953 a9b3f29d Antony Chazapis
        if user != account:
954 a9b3f29d Antony Chazapis
            raise NotAllowedError
955 2715ade4 Sofia Papagiannaki
956 a9b3f29d Antony Chazapis
        if until is not None:
957 a9b3f29d Antony Chazapis
            path = '/'.join((account, container, name))
958 c915d3bf Antony Chazapis
            node = self.node.node_lookup(path)
959 c915d3bf Antony Chazapis
            if node is None:
960 c915d3bf Antony Chazapis
                return
961 813e42e5 Antony Chazapis
            hashes = []
962 813e42e5 Antony Chazapis
            size = 0
963 388ea25f Sofia Papagiannaki
            serials = []
964 388ea25f Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_NORMAL)
965 813e42e5 Antony Chazapis
            hashes += h
966 813e42e5 Antony Chazapis
            size += s
967 388ea25f Sofia Papagiannaki
            serials += v
968 388ea25f Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_HISTORY)
969 813e42e5 Antony Chazapis
            hashes += h
970 813e42e5 Antony Chazapis
            size += s
971 388ea25f Sofia Papagiannaki
            serials += v
972 04230536 Antony Chazapis
            for h in hashes:
973 04230536 Antony Chazapis
                self.store.map_delete(h)
974 4a1c29ea Antony Chazapis
            self.node.node_purge(node, until, CLUSTER_DELETED)
975 a9b3f29d Antony Chazapis
            try:
976 c915d3bf Antony Chazapis
                props = self._get_version(node)
977 a9b3f29d Antony Chazapis
            except NameError:
978 0f9d752c Antony Chazapis
                self.permissions.access_clear(path)
979 388ea25f Sofia Papagiannaki
            self._report_size_change(user, account, -size,
980 7ed99da8 root
                                    {'action': 'object purge', 'path': path,
981 b1dadd0e Sofia Papagiannaki
                                     'versions': ','.join(str(i) for i in serials)})
982 a9b3f29d Antony Chazapis
            return
983 2715ade4 Sofia Papagiannaki
984 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
985 33b4e4a6 Antony Chazapis
        src_version_id, dest_version_id = self._put_version_duplicate(user, node, size=0, type='', hash=None, checksum='', cluster=CLUSTER_DELETED)
986 813e42e5 Antony Chazapis
        del_size = self._apply_versioning(account, container, src_version_id)
987 b1dadd0e Sofia Papagiannaki
        self._report_size_change(user, account, -del_size,
988 b1dadd0e Sofia Papagiannaki
                                 {'action': 'object delete', 'path': path,
989 b1dadd0e Sofia Papagiannaki
                                  'versions': ','.join([str(dest_version_id)])})
990 2715ade4 Sofia Papagiannaki
        self._report_object_change(
991 2715ade4 Sofia Papagiannaki
            user, account, path, details={'action': 'object delete'})
992 0f9d752c Antony Chazapis
        self.permissions.access_clear(path)
993 2715ade4 Sofia Papagiannaki
994 4d15c94e Sofia Papagiannaki
        if delimiter:
995 4d15c94e Sofia Papagiannaki
            prefix = name + delimiter if not name.endswith(delimiter) else name
996 e46b2bcf Sofia Papagiannaki
            src_names = self._list_objects_no_limit(user, account, container, prefix, delimiter=None, virtual=False, domain=None, keys=[], shared=False, until=None, size_range=None, all_props=True, public=False)
997 4d15c94e Sofia Papagiannaki
            paths = []
998 4d15c94e Sofia Papagiannaki
            for t in src_names:
999 2715ade4 Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
1000 2715ade4 Sofia Papagiannaki
                node = t[2]
1001 4d15c94e Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(user, node, size=0, type='', hash=None, checksum='', cluster=CLUSTER_DELETED)
1002 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
1003 2715ade4 Sofia Papagiannaki
                    account, container, src_version_id)
1004 b1dadd0e Sofia Papagiannaki
                self._report_size_change(user, account, -del_size,
1005 b1dadd0e Sofia Papagiannaki
                                         {'action': 'object delete',
1006 b1dadd0e Sofia Papagiannaki
                                          'path': path,
1007 b1dadd0e Sofia Papagiannaki
                                          'versions': ','.join([str(dest_version_id)])})
1008 2715ade4 Sofia Papagiannaki
                self._report_object_change(
1009 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
1010 4d15c94e Sofia Papagiannaki
                paths.append(path)
1011 4d15c94e Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
1012 2715ade4 Sofia Papagiannaki
1013 4d15c94e Sofia Papagiannaki
    @backend_method
1014 4d15c94e Sofia Papagiannaki
    def delete_object(self, user, account, container, name, until=None, prefix='', delimiter=None):
1015 dff7b6f1 Sofia Papagiannaki
        """Delete/purge an object."""
1016 2715ade4 Sofia Papagiannaki
1017 2715ade4 Sofia Papagiannaki
        logger.debug("delete_object: %s %s %s %s %s %s %s", user,
1018 2715ade4 Sofia Papagiannaki
                     account, container, name, until, prefix, delimiter)
1019 4d15c94e Sofia Papagiannaki
        self._delete_object(user, account, container, name, until, delimiter)
1020 2715ade4 Sofia Papagiannaki
1021 dff7b6f1 Sofia Papagiannaki
    @backend_method
1022 62f915a1 Antony Chazapis
    def list_versions(self, user, account, container, name):
1023 62f915a1 Antony Chazapis
        """Return a list of all (version, version_timestamp) tuples for an object."""
1024 2715ade4 Sofia Papagiannaki
1025 2715ade4 Sofia Papagiannaki
        logger.debug(
1026 2715ade4 Sofia Papagiannaki
            "list_versions: %s %s %s %s", user, account, container, name)
1027 62f915a1 Antony Chazapis
        self._can_read(user, account, container, name)
1028 60b8a083 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1029 97d45f69 Antony Chazapis
        versions = self.node.node_get_versions(node)
1030 97d45f69 Antony Chazapis
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if x[self.CLUSTER] != CLUSTER_DELETED]
1031 2715ade4 Sofia Papagiannaki
1032 bb4eafc6 Antony Chazapis
    @backend_method
1033 37bee317 Antony Chazapis
    def get_uuid(self, user, uuid):
1034 37bee317 Antony Chazapis
        """Return the (account, container, name) for the UUID given."""
1035 2715ade4 Sofia Papagiannaki
1036 fe2db49d Sofia Papagiannaki
        logger.debug("get_uuid: %s %s", user, uuid)
1037 37bee317 Antony Chazapis
        info = self.node.latest_uuid(uuid)
1038 37bee317 Antony Chazapis
        if info is None:
1039 37bee317 Antony Chazapis
            raise NameError
1040 37bee317 Antony Chazapis
        path, serial = info
1041 37bee317 Antony Chazapis
        account, container, name = path.split('/', 2)
1042 37bee317 Antony Chazapis
        self._can_read(user, account, container, name)
1043 37bee317 Antony Chazapis
        return (account, container, name)
1044 2715ade4 Sofia Papagiannaki
1045 37bee317 Antony Chazapis
    @backend_method
1046 bb4eafc6 Antony Chazapis
    def get_public(self, user, public):
1047 bb4eafc6 Antony Chazapis
        """Return the (account, container, name) for the public id given."""
1048 2715ade4 Sofia Papagiannaki
1049 fe2db49d Sofia Papagiannaki
        logger.debug("get_public: %s %s", user, public)
1050 bb4eafc6 Antony Chazapis
        if public is None or public < ULTIMATE_ANSWER:
1051 bb4eafc6 Antony Chazapis
            raise NameError
1052 bb4eafc6 Antony Chazapis
        path = self.permissions.public_path(public - ULTIMATE_ANSWER)
1053 37bee317 Antony Chazapis
        if path is None:
1054 37bee317 Antony Chazapis
            raise NameError
1055 bb4eafc6 Antony Chazapis
        account, container, name = path.split('/', 2)
1056 bb4eafc6 Antony Chazapis
        self._can_read(user, account, container, name)
1057 bb4eafc6 Antony Chazapis
        return (account, container, name)
1058 2715ade4 Sofia Papagiannaki
1059 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
1060 a9b3f29d Antony Chazapis
    def get_block(self, hash):
1061 a9b3f29d Antony Chazapis
        """Return a block's data."""
1062 2715ade4 Sofia Papagiannaki
1063 a9b3f29d Antony Chazapis
        logger.debug("get_block: %s", hash)
1064 7ca7bb08 Antony Chazapis
        block = self.store.block_get(binascii.unhexlify(hash))
1065 7ca7bb08 Antony Chazapis
        if not block:
1066 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Block does not exist')
1067 7ca7bb08 Antony Chazapis
        return block
1068 2715ade4 Sofia Papagiannaki
1069 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
1070 a9b3f29d Antony Chazapis
    def put_block(self, data):
1071 60b8a083 Antony Chazapis
        """Store a block and return the hash."""
1072 2715ade4 Sofia Papagiannaki
1073 a9b3f29d Antony Chazapis
        logger.debug("put_block: %s", len(data))
1074 7ca7bb08 Antony Chazapis
        return binascii.hexlify(self.store.block_put(data))
1075 2715ade4 Sofia Papagiannaki
1076 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
1077 a9b3f29d Antony Chazapis
    def update_block(self, hash, data, offset=0):
1078 a9b3f29d Antony Chazapis
        """Update a known block and return the hash."""
1079 2715ade4 Sofia Papagiannaki
1080 a9b3f29d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
1081 a9b3f29d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
1082 a9b3f29d Antony Chazapis
            return self.put_block(data)
1083 7ca7bb08 Antony Chazapis
        h = self.store.block_update(binascii.unhexlify(hash), offset, data)
1084 a9b3f29d Antony Chazapis
        return binascii.hexlify(h)
1085 2715ade4 Sofia Papagiannaki
1086 44ad5860 Antony Chazapis
    # Path functions.
1087 2715ade4 Sofia Papagiannaki
1088 37bee317 Antony Chazapis
    def _generate_uuid(self):
1089 37bee317 Antony Chazapis
        return str(uuidlib.uuid4())
1090 2715ade4 Sofia Papagiannaki
1091 b9064632 Antony Chazapis
    def _put_object_node(self, path, parent, name):
1092 c915d3bf Antony Chazapis
        path = '/'.join((path, name))
1093 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1094 c915d3bf Antony Chazapis
        if node is None:
1095 c915d3bf Antony Chazapis
            node = self.node.node_create(parent, path)
1096 c915d3bf Antony Chazapis
        return path, node
1097 2715ade4 Sofia Papagiannaki
1098 62f915a1 Antony Chazapis
    def _put_path(self, user, parent, path):
1099 62f915a1 Antony Chazapis
        node = self.node.node_create(parent, path)
1100 2715ade4 Sofia Papagiannaki
        self.node.version_create(node, None, 0, '', None, user,
1101 2715ade4 Sofia Papagiannaki
                                 self._generate_uuid(), '', CLUSTER_NORMAL)
1102 62f915a1 Antony Chazapis
        return node
1103 2715ade4 Sofia Papagiannaki
1104 44ad5860 Antony Chazapis
    def _lookup_account(self, account, create=True):
1105 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
1106 44ad5860 Antony Chazapis
        if node is None and create:
1107 2715ade4 Sofia Papagiannaki
            node = self._put_path(
1108 2715ade4 Sofia Papagiannaki
                account, self.ROOTNODE, account)  # User is account.
1109 c915d3bf Antony Chazapis
        return account, node
1110 2715ade4 Sofia Papagiannaki
1111 44ad5860 Antony Chazapis
    def _lookup_container(self, account, container):
1112 c915d3bf Antony Chazapis
        path = '/'.join((account, container))
1113 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1114 44ad5860 Antony Chazapis
        if node is None:
1115 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Container does not exist')
1116 c915d3bf Antony Chazapis
        return path, node
1117 2715ade4 Sofia Papagiannaki
1118 44ad5860 Antony Chazapis
    def _lookup_object(self, account, container, name):
1119 c915d3bf Antony Chazapis
        path = '/'.join((account, container, name))
1120 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1121 44ad5860 Antony Chazapis
        if node is None:
1122 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Object does not exist')
1123 c915d3bf Antony Chazapis
        return path, node
1124 2715ade4 Sofia Papagiannaki
1125 cf4a7a7b Sofia Papagiannaki
    def _lookup_objects(self, paths):
1126 7efc9f86 Sofia Papagiannaki
        nodes = self.node.node_lookup_bulk(paths)
1127 cf4a7a7b Sofia Papagiannaki
        return paths, nodes
1128 2715ade4 Sofia Papagiannaki
1129 44ad5860 Antony Chazapis
    def _get_properties(self, node, until=None):
1130 44ad5860 Antony Chazapis
        """Return properties until the timestamp given."""
1131 2715ade4 Sofia Papagiannaki
1132 44ad5860 Antony Chazapis
        before = until if until is not None else inf
1133 44ad5860 Antony Chazapis
        props = self.node.version_lookup(node, before, CLUSTER_NORMAL)
1134 44ad5860 Antony Chazapis
        if props is None and until is not None:
1135 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, before, CLUSTER_HISTORY)
1136 44ad5860 Antony Chazapis
        if props is None:
1137 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Path does not exist')
1138 44ad5860 Antony Chazapis
        return props
1139 2715ade4 Sofia Papagiannaki
1140 62f915a1 Antony Chazapis
    def _get_statistics(self, node, until=None):
1141 62f915a1 Antony Chazapis
        """Return count, sum of size and latest timestamp of everything under node."""
1142 2715ade4 Sofia Papagiannaki
1143 44ad5860 Antony Chazapis
        if until is None:
1144 62f915a1 Antony Chazapis
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1145 44ad5860 Antony Chazapis
        else:
1146 62f915a1 Antony Chazapis
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1147 62f915a1 Antony Chazapis
        if stats is None:
1148 62f915a1 Antony Chazapis
            stats = (0, 0, 0)
1149 62f915a1 Antony Chazapis
        return stats
1150 2715ade4 Sofia Papagiannaki
1151 44ad5860 Antony Chazapis
    def _get_version(self, node, version=None):
1152 44ad5860 Antony Chazapis
        if version is None:
1153 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1154 44ad5860 Antony Chazapis
            if props is None:
1155 7efc9f86 Sofia Papagiannaki
                raise ItemNotExists('Object does not exist')
1156 44ad5860 Antony Chazapis
        else:
1157 07afd277 Antony Chazapis
            try:
1158 07afd277 Antony Chazapis
                version = int(version)
1159 07afd277 Antony Chazapis
            except ValueError:
1160 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1161 44ad5860 Antony Chazapis
            props = self.node.version_get_properties(version)
1162 2c5363a0 Antony Chazapis
            if props is None or props[self.CLUSTER] == CLUSTER_DELETED:
1163 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1164 44ad5860 Antony Chazapis
        return props
1165 4d15c94e Sofia Papagiannaki
1166 7efc9f86 Sofia Papagiannaki
    def _get_versions(self, nodes):
1167 7efc9f86 Sofia Papagiannaki
        return self.node.version_lookup_bulk(nodes, inf, CLUSTER_NORMAL)
1168 2715ade4 Sofia Papagiannaki
1169 33b4e4a6 Antony Chazapis
    def _put_version_duplicate(self, user, node, src_node=None, size=None, type=None, hash=None, checksum=None, cluster=CLUSTER_NORMAL, is_copy=False):
1170 b9064632 Antony Chazapis
        """Create a new version of the node."""
1171 2715ade4 Sofia Papagiannaki
1172 2715ade4 Sofia Papagiannaki
        props = self.node.version_lookup(
1173 2715ade4 Sofia Papagiannaki
            node if src_node is None else src_node, inf, CLUSTER_NORMAL)
1174 b9064632 Antony Chazapis
        if props is not None:
1175 b9064632 Antony Chazapis
            src_version_id = props[self.SERIAL]
1176 b9064632 Antony Chazapis
            src_hash = props[self.HASH]
1177 b9064632 Antony Chazapis
            src_size = props[self.SIZE]
1178 66ce2ca5 Antony Chazapis
            src_type = props[self.TYPE]
1179 33b4e4a6 Antony Chazapis
            src_checksum = props[self.CHECKSUM]
1180 44ad5860 Antony Chazapis
        else:
1181 b9064632 Antony Chazapis
            src_version_id = None
1182 b9064632 Antony Chazapis
            src_hash = None
1183 b9064632 Antony Chazapis
            src_size = 0
1184 66ce2ca5 Antony Chazapis
            src_type = ''
1185 33b4e4a6 Antony Chazapis
            src_checksum = ''
1186 2715ade4 Sofia Papagiannaki
        if size is None:  # Set metadata.
1187 2715ade4 Sofia Papagiannaki
            hash = src_hash  # This way hash can be set to None (account or container).
1188 b9064632 Antony Chazapis
            size = src_size
1189 66ce2ca5 Antony Chazapis
        if type is None:
1190 66ce2ca5 Antony Chazapis
            type = src_type
1191 33b4e4a6 Antony Chazapis
        if checksum is None:
1192 33b4e4a6 Antony Chazapis
            checksum = src_checksum
1193 2715ade4 Sofia Papagiannaki
        uuid = self._generate_uuid(
1194 2715ade4 Sofia Papagiannaki
        ) if (is_copy or src_version_id is None) else props[self.UUID]
1195 2715ade4 Sofia Papagiannaki
1196 1730b3bf chazapis
        if src_node is None:
1197 1730b3bf chazapis
            pre_version_id = src_version_id
1198 1730b3bf chazapis
        else:
1199 1730b3bf chazapis
            pre_version_id = None
1200 1730b3bf chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1201 1730b3bf chazapis
            if props is not None:
1202 1730b3bf chazapis
                pre_version_id = props[self.SERIAL]
1203 1730b3bf chazapis
        if pre_version_id is not None:
1204 1730b3bf chazapis
            self.node.version_recluster(pre_version_id, CLUSTER_HISTORY)
1205 2715ade4 Sofia Papagiannaki
1206 33b4e4a6 Antony Chazapis
        dest_version_id, mtime = self.node.version_create(node, hash, size, type, src_version_id, user, uuid, checksum, cluster)
1207 1730b3bf chazapis
        return pre_version_id, dest_version_id
1208 2715ade4 Sofia Papagiannaki
1209 4819d34f Antony Chazapis
    def _put_metadata_duplicate(self, src_version_id, dest_version_id, domain, meta, replace=False):
1210 4819d34f Antony Chazapis
        if src_version_id is not None:
1211 4819d34f Antony Chazapis
            self.node.attribute_copy(src_version_id, dest_version_id)
1212 4819d34f Antony Chazapis
        if not replace:
1213 2715ade4 Sofia Papagiannaki
            self.node.attribute_del(dest_version_id, domain, (
1214 2715ade4 Sofia Papagiannaki
                k for k, v in meta.iteritems() if v == ''))
1215 2715ade4 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, (
1216 2715ade4 Sofia Papagiannaki
                (k, v) for k, v in meta.iteritems() if v != ''))
1217 4819d34f Antony Chazapis
        else:
1218 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain)
1219 2715ade4 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, ((
1220 2715ade4 Sofia Papagiannaki
                k, v) for k, v in meta.iteritems()))
1221 2715ade4 Sofia Papagiannaki
1222 4819d34f Antony Chazapis
    def _put_metadata(self, user, node, domain, meta, replace=False):
1223 44ad5860 Antony Chazapis
        """Create a new version and store metadata."""
1224 2715ade4 Sofia Papagiannaki
1225 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1226 2715ade4 Sofia Papagiannaki
            user, node)
1227 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1228 2715ade4 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, meta, replace)
1229 5cc484e1 Antony Chazapis
        return src_version_id, dest_version_id
1230 2715ade4 Sofia Papagiannaki
1231 60b8a083 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
1232 60b8a083 Antony Chazapis
        start = 0
1233 60b8a083 Antony Chazapis
        if marker:
1234 60b8a083 Antony Chazapis
            try:
1235 60b8a083 Antony Chazapis
                start = listing.index(marker) + 1
1236 60b8a083 Antony Chazapis
            except ValueError:
1237 60b8a083 Antony Chazapis
                pass
1238 60b8a083 Antony Chazapis
        if not limit or limit > 10000:
1239 60b8a083 Antony Chazapis
            limit = 10000
1240 60b8a083 Antony Chazapis
        return start, limit
1241 2715ade4 Sofia Papagiannaki
1242 371d907a Antony Chazapis
    def _list_object_properties(self, parent, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], until=None, size_range=None, allowed=[], all_props=False):
1243 60b8a083 Antony Chazapis
        cont_prefix = path + '/'
1244 60b8a083 Antony Chazapis
        prefix = cont_prefix + prefix
1245 60b8a083 Antony Chazapis
        start = cont_prefix + marker if marker else None
1246 60b8a083 Antony Chazapis
        before = until if until is not None else inf
1247 4819d34f Antony Chazapis
        filterq = keys if domain else []
1248 7ff57991 Antony Chazapis
        sizeq = size_range
1249 2715ade4 Sofia Papagiannaki
1250 371d907a Antony Chazapis
        objects, prefixes = self.node.latest_version_list(parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED, allowed, domain, filterq, sizeq, all_props)
1251 60b8a083 Antony Chazapis
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1252 43be9afd Sofia Papagiannaki
        objects.sort(key=lambda x: x[0])
1253 371d907a Antony Chazapis
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
1254 cf4a7a7b Sofia Papagiannaki
        return objects
1255 2715ade4 Sofia Papagiannaki
1256 813e42e5 Antony Chazapis
    # Reporting functions.
1257 2715ade4 Sofia Papagiannaki
1258 813e42e5 Antony Chazapis
    def _report_size_change(self, user, account, size, details={}):
1259 813e42e5 Antony Chazapis
        account_node = self._lookup_account(account, True)[1]
1260 813e42e5 Antony Chazapis
        total = self._get_statistics(account_node)[1]
1261 813e42e5 Antony Chazapis
        details.update({'user': user, 'total': total})
1262 2715ade4 Sofia Papagiannaki
        logger.debug(
1263 2715ade4 Sofia Papagiannaki
            "_report_size_change: %s %s %s %s", user, account, size, details)
1264 2715ade4 Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',), 
1265 7ed99da8 root
                              account, QUEUE_INSTANCE_ID, 'diskspace',
1266 7ed99da8 root
                              float(size), details))
1267 7ed99da8 root
1268 73fbe301 Sofia Papagiannaki
        if not self.quotaholder_url:
1269 73fbe301 Sofia Papagiannaki
            return
1270 73fbe301 Sofia Papagiannaki
        
1271 7ed99da8 root
        serial = self.quotaholder.issue_commission(
1272 7ed99da8 root
                context     =   {},
1273 019d9e19 Sofia Papagiannaki
                target      =   user.uuid,
1274 7ed99da8 root
                key         =   '1',
1275 7ed99da8 root
                clientkey   =   'pithos',
1276 7ed99da8 root
                ownerkey    =   '',
1277 56a42b0b Sofia Papagiannaki
                        name        =   details['path'] if 'path' in details else '',
1278 90311d98 root
                provisions  =   (('pithos+', 'pithos+.diskspace', size),)
1279 7ed99da8 root
        )
1280 7ed99da8 root
        self.serials.append(serial)
1281 0307b47f Georgios D. Tsoukalas
1282 39ef6f41 Antony Chazapis
    def _report_object_change(self, user, account, path, details={}):
1283 b82d3277 Sofia Papagiannaki
        details.update({'user': user})
1284 2715ade4 Sofia Papagiannaki
        logger.debug("_report_object_change: %s %s %s %s", user,
1285 2715ade4 Sofia Papagiannaki
                     account, path, details)
1286 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',),
1287 b1dadd0e Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'object', path, details))
1288 2715ade4 Sofia Papagiannaki
1289 a74ba506 Sofia Papagiannaki
    def _report_sharing_change(self, user, account, path, details={}):
1290 2715ade4 Sofia Papagiannaki
        logger.debug("_report_permissions_change: %s %s %s %s",
1291 2715ade4 Sofia Papagiannaki
                     user, account, path, details)
1292 a74ba506 Sofia Papagiannaki
        details.update({'user': user})
1293 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',),
1294 b1dadd0e Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'sharing', path, details))
1295 2715ade4 Sofia Papagiannaki
1296 60b8a083 Antony Chazapis
    # Policy functions.
1297 2715ade4 Sofia Papagiannaki
1298 60b8a083 Antony Chazapis
    def _check_policy(self, policy):
1299 60b8a083 Antony Chazapis
        for k in policy.keys():
1300 60b8a083 Antony Chazapis
            if policy[k] == '':
1301 60b8a083 Antony Chazapis
                policy[k] = self.default_policy.get(k)
1302 60b8a083 Antony Chazapis
        for k, v in policy.iteritems():
1303 60b8a083 Antony Chazapis
            if k == 'quota':
1304 2715ade4 Sofia Papagiannaki
                q = int(v)  # May raise ValueError.
1305 60b8a083 Antony Chazapis
                if q < 0:
1306 60b8a083 Antony Chazapis
                    raise ValueError
1307 60b8a083 Antony Chazapis
            elif k == 'versioning':
1308 5cc484e1 Antony Chazapis
                if v not in ['auto', 'none']:
1309 60b8a083 Antony Chazapis
                    raise ValueError
1310 60b8a083 Antony Chazapis
            else:
1311 60b8a083 Antony Chazapis
                raise ValueError
1312 2715ade4 Sofia Papagiannaki
1313 b2832c6a Antony Chazapis
    def _put_policy(self, node, policy, replace):
1314 b2832c6a Antony Chazapis
        if replace:
1315 b2832c6a Antony Chazapis
            for k, v in self.default_policy.iteritems():
1316 b2832c6a Antony Chazapis
                if k not in policy:
1317 b2832c6a Antony Chazapis
                    policy[k] = v
1318 b2832c6a Antony Chazapis
        self.node.policy_set(node, policy)
1319 2715ade4 Sofia Papagiannaki
1320 b9064632 Antony Chazapis
    def _get_policy(self, node):
1321 b9064632 Antony Chazapis
        policy = self.default_policy.copy()
1322 b9064632 Antony Chazapis
        policy.update(self.node.policy_get(node))
1323 b9064632 Antony Chazapis
        return policy
1324 2715ade4 Sofia Papagiannaki
1325 5cc484e1 Antony Chazapis
    def _apply_versioning(self, account, container, version_id):
1326 813e42e5 Antony Chazapis
        """Delete the provided version if such is the policy.
1327 813e42e5 Antony Chazapis
           Return size of object removed.
1328 813e42e5 Antony Chazapis
        """
1329 2715ade4 Sofia Papagiannaki
1330 5cc484e1 Antony Chazapis
        if version_id is None:
1331 813e42e5 Antony Chazapis
            return 0
1332 5cc484e1 Antony Chazapis
        path, node = self._lookup_container(account, container)
1333 5cc484e1 Antony Chazapis
        versioning = self._get_policy(node)['versioning']
1334 1dd34bdd Sofia Papagiannaki
        if versioning != 'auto':
1335 813e42e5 Antony Chazapis
            hash, size = self.node.version_remove(version_id)
1336 5161c672 Antony Chazapis
            self.store.map_delete(hash)
1337 813e42e5 Antony Chazapis
            return size
1338 1dd34bdd Sofia Papagiannaki
        elif self.free_versioning:
1339 fff37615 Sofia Papagiannaki
            return self.node.version_get_properties(
1340 fff37615 Sofia Papagiannaki
                version_id, keys=('size',))[0]
1341 813e42e5 Antony Chazapis
        return 0
1342 2715ade4 Sofia Papagiannaki
1343 a9b3f29d Antony Chazapis
    # Access control functions.
1344 2715ade4 Sofia Papagiannaki
1345 a9b3f29d Antony Chazapis
    def _check_groups(self, groups):
1346 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in groups')
1347 a9b3f29d Antony Chazapis
        pass
1348 2715ade4 Sofia Papagiannaki
1349 a9b3f29d Antony Chazapis
    def _check_permissions(self, path, permissions):
1350 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in permissions')
1351 5e068361 Antony Chazapis
        pass
1352 2715ade4 Sofia Papagiannaki
1353 92da0e5a Antony Chazapis
    def _get_formatted_paths(self, paths):
1354 92da0e5a Antony Chazapis
        formatted = []
1355 92da0e5a Antony Chazapis
        for p in paths:
1356 92da0e5a Antony Chazapis
            node = self.node.node_lookup(p)
1357 a8b82467 Sofia Papagiannaki
            props = None
1358 92da0e5a Antony Chazapis
            if node is not None:
1359 92da0e5a Antony Chazapis
                props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1360 92da0e5a Antony Chazapis
            if props is not None:
1361 692485cc Antony Chazapis
                if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1362 d57eaad4 Antony Chazapis
                    formatted.append((p.rstrip('/') + '/', self.MATCH_PREFIX))
1363 d57eaad4 Antony Chazapis
                formatted.append((p, self.MATCH_EXACT))
1364 92da0e5a Antony Chazapis
        return formatted
1365 2715ade4 Sofia Papagiannaki
1366 5e068361 Antony Chazapis
    def _get_permissions_path(self, account, container, name):
1367 5e068361 Antony Chazapis
        path = '/'.join((account, container, name))
1368 5e068361 Antony Chazapis
        permission_paths = self.permissions.access_inherit(path)
1369 5e068361 Antony Chazapis
        permission_paths.sort()
1370 5e068361 Antony Chazapis
        permission_paths.reverse()
1371 5e068361 Antony Chazapis
        for p in permission_paths:
1372 5e068361 Antony Chazapis
            if p == path:
1373 5e068361 Antony Chazapis
                return p
1374 5e068361 Antony Chazapis
            else:
1375 71dbc012 Antony Chazapis
                if p.count('/') < 2:
1376 71dbc012 Antony Chazapis
                    continue
1377 92da0e5a Antony Chazapis
                node = self.node.node_lookup(p)
1378 d62295ba Sofia Papagiannaki
                props = None
1379 92da0e5a Antony Chazapis
                if node is not None:
1380 92da0e5a Antony Chazapis
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1381 92da0e5a Antony Chazapis
                if props is not None:
1382 692485cc Antony Chazapis
                    if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1383 5e068361 Antony Chazapis
                        return p
1384 5e068361 Antony Chazapis
        return None
1385 2715ade4 Sofia Papagiannaki
1386 6f4bce7b Antony Chazapis
    def _can_read(self, user, account, container, name):
1387 a9b3f29d Antony Chazapis
        if user == account:
1388 a9b3f29d Antony Chazapis
            return True
1389 a9b3f29d Antony Chazapis
        path = '/'.join((account, container, name))
1390 aeb2b64f Antony Chazapis
        if self.permissions.public_get(path) is not None:
1391 71dbc012 Antony Chazapis
            return True
1392 5e068361 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1393 71dbc012 Antony Chazapis
        if not path:
1394 71dbc012 Antony Chazapis
            raise NotAllowedError
1395 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.READ, user) and not self.permissions.access_check(path, self.WRITE, user):
1396 a9b3f29d Antony Chazapis
            raise NotAllowedError
1397 2715ade4 Sofia Papagiannaki
1398 a9b3f29d Antony Chazapis
    def _can_write(self, user, account, container, name):
1399 6f4bce7b Antony Chazapis
        if user == account:
1400 6f4bce7b Antony Chazapis
            return True
1401 6f4bce7b Antony Chazapis
        path = '/'.join((account, container, name))
1402 71dbc012 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1403 71dbc012 Antony Chazapis
        if not path:
1404 71dbc012 Antony Chazapis
            raise NotAllowedError
1405 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.WRITE, user):
1406 a9b3f29d Antony Chazapis
            raise NotAllowedError
1407 2715ade4 Sofia Papagiannaki
1408 a9b3f29d Antony Chazapis
    def _allowed_accounts(self, user):
1409 a9b3f29d Antony Chazapis
        allow = set()
1410 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user):
1411 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 1)[0])
1412 a9b3f29d Antony Chazapis
        return sorted(allow)
1413 2715ade4 Sofia Papagiannaki
1414 a9b3f29d Antony Chazapis
    def _allowed_containers(self, user, account):
1415 a9b3f29d Antony Chazapis
        allow = set()
1416 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user, account):
1417 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 2)[1])
1418 a9b3f29d Antony Chazapis
        return sorted(allow)