Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 1e47e49d

History | View | Annotate | Download (78.3 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 37bee317 Antony Chazapis
import uuid as uuidlib
36 a9b3f29d Antony Chazapis
import logging
37 6e147ecc Antony Chazapis
import hashlib
38 a9b3f29d Antony Chazapis
import binascii
39 a9b3f29d Antony Chazapis
40 ebdbac7a Sofia Papagiannaki
from collections import defaultdict
41 9e3a38bb Sofia Papagiannaki
from functools import wraps, partial
42 1f96b68d Sofia Papagiannaki
from traceback import format_exc
43 1f96b68d Sofia Papagiannaki
44 7ee27246 Georgios D. Tsoukalas
try:
45 7ee27246 Georgios D. Tsoukalas
    from astakosclient import AstakosClient
46 7ee27246 Georgios D. Tsoukalas
except ImportError:
47 7ee27246 Georgios D. Tsoukalas
    AstakosClient = None
48 0307b47f Georgios D. Tsoukalas
49 19ddd41b Sofia Papagiannaki
from base import (DEFAULT_ACCOUNT_QUOTA, DEFAULT_CONTAINER_QUOTA,
50 19ddd41b Sofia Papagiannaki
                  DEFAULT_CONTAINER_VERSIONING, NotAllowedError, QuotaError,
51 19ddd41b Sofia Papagiannaki
                  BaseBackend, AccountExists, ContainerExists, AccountNotEmpty,
52 3a5994a8 Sofia Papagiannaki
                  ContainerNotEmpty, ItemNotExists, VersionNotExists,
53 1e47e49d Chrysostomos Nanakos
                  InvalidHash, IllegalOperationError)
54 a9b3f29d Antony Chazapis
55 7ee27246 Georgios D. Tsoukalas
56 7ee27246 Georgios D. Tsoukalas
class DisabledAstakosClient(object):
57 7ee27246 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
58 7ee27246 Georgios D. Tsoukalas
        self.args = args
59 7ee27246 Georgios D. Tsoukalas
        self.kwargs = kwargs
60 7ee27246 Georgios D. Tsoukalas
61 7ee27246 Georgios D. Tsoukalas
    def __getattr__(self, name):
62 7ee27246 Georgios D. Tsoukalas
        m = ("AstakosClient has been disabled, "
63 7ee27246 Georgios D. Tsoukalas
             "yet an attempt to access it was made")
64 7ee27246 Georgios D. Tsoukalas
        raise AssertionError(m)
65 7ee27246 Georgios D. Tsoukalas
66 7ee27246 Georgios D. Tsoukalas
67 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
68 2715ade4 Sofia Papagiannaki
69 6e147ecc Antony Chazapis
class HashMap(list):
70 6e147ecc Antony Chazapis
71 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
72 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
73 6e147ecc Antony Chazapis
        self.blocksize = blocksize
74 6e147ecc Antony Chazapis
        self.blockhash = blockhash
75 6e147ecc Antony Chazapis
76 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
77 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
78 6e147ecc Antony Chazapis
        h.update(v)
79 6e147ecc Antony Chazapis
        return h.digest()
80 6e147ecc Antony Chazapis
81 6e147ecc Antony Chazapis
    def hash(self):
82 6e147ecc Antony Chazapis
        if len(self) == 0:
83 6e147ecc Antony Chazapis
            return self._hash_raw('')
84 6e147ecc Antony Chazapis
        if len(self) == 1:
85 6e147ecc Antony Chazapis
            return self.__getitem__(0)
86 6e147ecc Antony Chazapis
87 6e147ecc Antony Chazapis
        h = list(self)
88 6e147ecc Antony Chazapis
        s = 2
89 6e147ecc Antony Chazapis
        while s < len(h):
90 6e147ecc Antony Chazapis
            s = s * 2
91 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
92 6e147ecc Antony Chazapis
        while len(h) > 1:
93 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
94 6e147ecc Antony Chazapis
        return h[0]
95 5a96180b Antony Chazapis
96 228de81b Antony Chazapis
# Default modules and settings.
97 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
98 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
99 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
100 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
101 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
102 369a7b41 Sofia Papagiannaki
DEFAULT_BLOCK_SIZE = 4 * 1024 * 1024  # 4MB
103 369a7b41 Sofia Papagiannaki
DEFAULT_HASH_ALGORITHM = 'sha256'
104 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
105 29148653 Sofia Papagiannaki
DEFAULT_BLOCK_PARAMS = {'mappool': None, 'blockpool': None}
106 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
107 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
108 4a105ce2 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_ALPHABET = ('0123456789'
109 4a105ce2 Sofia Papagiannaki
                               'abcdefghijklmnopqrstuvwxyz'
110 4a105ce2 Sofia Papagiannaki
                               'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
111 c77af544 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_SECURITY = 16
112 fa9cae7e Antony Chazapis
113 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
114 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
115 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
116 228de81b Antony Chazapis
117 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
118 44ad5860 Antony Chazapis
119 44ad5860 Antony Chazapis
inf = float('inf')
120 44ad5860 Antony Chazapis
121 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
122 bb4eafc6 Antony Chazapis
123 b17e5550 Giorgos Korfiatis
DEFAULT_SOURCE = 'system'
124 3b8f938b Sofia Papagiannaki
DEFAULT_DISKSPACE_RESOURCE = 'pithos.diskspace'
125 a9b3f29d Antony Chazapis
126 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
127 a9b3f29d Antony Chazapis
128 1c2fc0ff Antony Chazapis
129 5d022141 Sofia Papagiannaki
def backend_method(func):
130 5d022141 Sofia Papagiannaki
    @wraps(func)
131 5d022141 Sofia Papagiannaki
    def wrapper(self, *args, **kw):
132 5d022141 Sofia Papagiannaki
        # if we are inside a database transaction
133 5d022141 Sofia Papagiannaki
        # just proceed with the method execution
134 5d022141 Sofia Papagiannaki
        # otherwise manage a new transaction
135 5d022141 Sofia Papagiannaki
        if self.in_transaction:
136 5d022141 Sofia Papagiannaki
            return func(self, *args, **kw)
137 5d022141 Sofia Papagiannaki
138 5d022141 Sofia Papagiannaki
        try:
139 5d022141 Sofia Papagiannaki
            self.pre_exec()
140 5d022141 Sofia Papagiannaki
            result = func(self, *args, **kw)
141 5d022141 Sofia Papagiannaki
            success_status = True
142 5d022141 Sofia Papagiannaki
            return result
143 5d022141 Sofia Papagiannaki
        except:
144 5d022141 Sofia Papagiannaki
            success_status = False
145 5d022141 Sofia Papagiannaki
            raise
146 5d022141 Sofia Papagiannaki
        finally:
147 5d022141 Sofia Papagiannaki
            self.post_exec(success_status)
148 5d022141 Sofia Papagiannaki
    return wrapper
149 5d022141 Sofia Papagiannaki
150 5d022141 Sofia Papagiannaki
151 1f96b68d Sofia Papagiannaki
def debug_method(func):
152 1f96b68d Sofia Papagiannaki
    @wraps(func)
153 1f96b68d Sofia Papagiannaki
    def wrapper(self, *args, **kw):
154 1f96b68d Sofia Papagiannaki
        try:
155 1f96b68d Sofia Papagiannaki
            result = func(self, *args, **kw)
156 1f96b68d Sofia Papagiannaki
            return result
157 1f96b68d Sofia Papagiannaki
        except:
158 1f96b68d Sofia Papagiannaki
            result = format_exc()
159 1f96b68d Sofia Papagiannaki
            raise
160 1f96b68d Sofia Papagiannaki
        finally:
161 b1aca3e6 Sofia Papagiannaki
            all_args = map(repr, args)
162 1f96b68d Sofia Papagiannaki
            map(all_args.append, ('%s=%s' % (k, v) for k, v in kw.iteritems()))
163 1f96b68d Sofia Papagiannaki
            logger.debug(">>> %s(%s) <<< %s" % (
164 1f96b68d Sofia Papagiannaki
                func.__name__, ', '.join(all_args).rstrip(', '), result))
165 1f96b68d Sofia Papagiannaki
    return wrapper
166 1f96b68d Sofia Papagiannaki
167 1f96b68d Sofia Papagiannaki
168 ebdbac7a Sofia Papagiannaki
def check_allowed_paths(action):
169 ebdbac7a Sofia Papagiannaki
    """Decorator for backend methods checking path access granted to user.
170 ebdbac7a Sofia Papagiannaki

171 ebdbac7a Sofia Papagiannaki
    The 1st argument of the decorated method is expected to be a
172 ebdbac7a Sofia Papagiannaki
    ModularBackend instance, the 2nd the user performing the request and
173 ebdbac7a Sofia Papagiannaki
    the path join of the rest arguments is supposed to be the requested path.
174 ebdbac7a Sofia Papagiannaki

175 ebdbac7a Sofia Papagiannaki
    The decorator checks whether the requested path is among the user's allowed
176 ebdbac7a Sofia Papagiannaki
    cached paths.
177 ebdbac7a Sofia Papagiannaki
    If this is the case, the decorator returns immediately to reduce the
178 ebdbac7a Sofia Papagiannaki
    interactions with the database.
179 ebdbac7a Sofia Papagiannaki
    Otherwise, it proceeds with the execution of the decorated method and if
180 ebdbac7a Sofia Papagiannaki
    the method returns successfully (no exceptions are raised), the requested
181 ebdbac7a Sofia Papagiannaki
    path is added to the user's cached allowed paths.
182 ebdbac7a Sofia Papagiannaki

183 ebdbac7a Sofia Papagiannaki
    :param action: (int) 0 for reads / 1 for writes
184 ebdbac7a Sofia Papagiannaki
    :raises NotAllowedError: the user does not have access to the path
185 ebdbac7a Sofia Papagiannaki
    """
186 ebdbac7a Sofia Papagiannaki
    def decorator(func):
187 ebdbac7a Sofia Papagiannaki
        @wraps(func)
188 ebdbac7a Sofia Papagiannaki
        def wrapper(self, *args):
189 ebdbac7a Sofia Papagiannaki
            user = args[0]
190 ebdbac7a Sofia Papagiannaki
            if action == self.READ:
191 ebdbac7a Sofia Papagiannaki
                d = self.read_allowed_paths
192 ebdbac7a Sofia Papagiannaki
            else:
193 ebdbac7a Sofia Papagiannaki
                d = self.write_allowed_paths
194 ebdbac7a Sofia Papagiannaki
            path = '/'.join(args[1:])
195 ebdbac7a Sofia Papagiannaki
            if path in d.get(user, []):
196 ebdbac7a Sofia Papagiannaki
                return  # access is already checked
197 ebdbac7a Sofia Papagiannaki
            else:
198 ebdbac7a Sofia Papagiannaki
                func(self, *args)   # proceed with access check
199 ebdbac7a Sofia Papagiannaki
                d[user].add(path)  # add path in the allowed user paths
200 ebdbac7a Sofia Papagiannaki
        return wrapper
201 ebdbac7a Sofia Papagiannaki
    return decorator
202 ebdbac7a Sofia Papagiannaki
203 ebdbac7a Sofia Papagiannaki
204 e5b77cde Sofia Papagiannaki
def list_method(func):
205 e5b77cde Sofia Papagiannaki
    @wraps(func)
206 e5b77cde Sofia Papagiannaki
    def wrapper(self, *args, **kw):
207 e5b77cde Sofia Papagiannaki
        marker = kw.get('marker')
208 e5b77cde Sofia Papagiannaki
        limit = kw.get('limit')
209 e5b77cde Sofia Papagiannaki
        result = func(self, *args, **kw)
210 e5b77cde Sofia Papagiannaki
        start, limit = self._list_limits(result, marker, limit)
211 e5b77cde Sofia Papagiannaki
        return result[start:start + limit]
212 e5b77cde Sofia Papagiannaki
    return wrapper
213 e5b77cde Sofia Papagiannaki
214 e5b77cde Sofia Papagiannaki
215 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
216 a9b3f29d Antony Chazapis
    """A modular backend.
217 2715ade4 Sofia Papagiannaki

218 e9363f82 Antony Chazapis
    Uses modules for SQL functions and storage.
219 a9b3f29d Antony Chazapis
    """
220 2715ade4 Sofia Papagiannaki
221 46286f5f Antony Chazapis
    def __init__(self, db_module=None, db_connection=None,
222 f3b65e8f Antony Chazapis
                 block_module=None, block_path=None, block_umask=None,
223 369a7b41 Sofia Papagiannaki
                 block_size=None, hash_algorithm=None,
224 3173699c Sofia Papagiannaki
                 queue_module=None, queue_hosts=None, queue_exchange=None,
225 f759041f Ilias Tsitsimpis
                 astakos_auth_url=None, service_token=None,
226 16f2673e Sofia Papagiannaki
                 astakosclient_poolsize=None,
227 56f3c759 Sofia Papagiannaki
                 free_versioning=True, block_params=None,
228 4a105ce2 Sofia Papagiannaki
                 public_url_security=None,
229 19ddd41b Sofia Papagiannaki
                 public_url_alphabet=None,
230 19ddd41b Sofia Papagiannaki
                 account_quota_policy=None,
231 19ddd41b Sofia Papagiannaki
                 container_quota_policy=None,
232 19ddd41b Sofia Papagiannaki
                 container_versioning_policy=None):
233 228de81b Antony Chazapis
        db_module = db_module or DEFAULT_DB_MODULE
234 228de81b Antony Chazapis
        db_connection = db_connection or DEFAULT_DB_CONNECTION
235 228de81b Antony Chazapis
        block_module = block_module or DEFAULT_BLOCK_MODULE
236 228de81b Antony Chazapis
        block_path = block_path or DEFAULT_BLOCK_PATH
237 f3b65e8f Antony Chazapis
        block_umask = block_umask or DEFAULT_BLOCK_UMASK
238 7f1f0464 Georgios D. Tsoukalas
        block_params = block_params or DEFAULT_BLOCK_PARAMS
239 369a7b41 Sofia Papagiannaki
        block_size = block_size or DEFAULT_BLOCK_SIZE
240 369a7b41 Sofia Papagiannaki
        hash_algorithm = hash_algorithm or DEFAULT_HASH_ALGORITHM
241 46286f5f Antony Chazapis
        #queue_module = queue_module or DEFAULT_QUEUE_MODULE
242 19ddd41b Sofia Papagiannaki
        account_quota_policy = account_quota_policy or DEFAULT_ACCOUNT_QUOTA
243 19ddd41b Sofia Papagiannaki
        container_quota_policy = container_quota_policy \
244 19ddd41b Sofia Papagiannaki
            or DEFAULT_CONTAINER_QUOTA
245 19ddd41b Sofia Papagiannaki
        container_versioning_policy = container_versioning_policy \
246 19ddd41b Sofia Papagiannaki
            or DEFAULT_CONTAINER_VERSIONING
247 19ddd41b Sofia Papagiannaki
248 19ddd41b Sofia Papagiannaki
        self.default_account_policy = {'quota': account_quota_policy}
249 19ddd41b Sofia Papagiannaki
        self.default_container_policy = {
250 19ddd41b Sofia Papagiannaki
            'quota': container_quota_policy,
251 19ddd41b Sofia Papagiannaki
            'versioning': container_versioning_policy
252 19ddd41b Sofia Papagiannaki
        }
253 f4fbb0fa Sofia Papagiannaki
        #queue_hosts = queue_hosts or DEFAULT_QUEUE_HOSTS
254 f4fbb0fa Sofia Papagiannaki
        #queue_exchange = queue_exchange or DEFAULT_QUEUE_EXCHANGE
255 7f1f0464 Georgios D. Tsoukalas
256 369a7b41 Sofia Papagiannaki
        self.public_url_security = (public_url_security or
257 29148653 Sofia Papagiannaki
                                    DEFAULT_PUBLIC_URL_SECURITY)
258 369a7b41 Sofia Papagiannaki
        self.public_url_alphabet = (public_url_alphabet or
259 29148653 Sofia Papagiannaki
                                    DEFAULT_PUBLIC_URL_ALPHABET)
260 56f3c759 Sofia Papagiannaki
261 369a7b41 Sofia Papagiannaki
        self.hash_algorithm = hash_algorithm
262 369a7b41 Sofia Papagiannaki
        self.block_size = block_size
263 b1dadd0e Sofia Papagiannaki
        self.free_versioning = free_versioning
264 2715ade4 Sofia Papagiannaki
265 46286f5f Antony Chazapis
        def load_module(m):
266 46286f5f Antony Chazapis
            __import__(m)
267 46286f5f Antony Chazapis
            return sys.modules[m]
268 2715ade4 Sofia Papagiannaki
269 46286f5f Antony Chazapis
        self.db_module = load_module(db_module)
270 46286f5f Antony Chazapis
        self.wrapper = self.db_module.DBWrapper(db_connection)
271 e9363f82 Antony Chazapis
        params = {'wrapper': self.wrapper}
272 e9363f82 Antony Chazapis
        self.permissions = self.db_module.Permissions(**params)
273 2c690fe9 Sofia Papagiannaki
        self.config = self.db_module.Config(**params)
274 16f2673e Sofia Papagiannaki
        self.commission_serials = self.db_module.QuotaholderSerial(**params)
275 e9363f82 Antony Chazapis
        for x in ['READ', 'WRITE']:
276 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
277 e9363f82 Antony Chazapis
        self.node = self.db_module.Node(**params)
278 adce84cd Sofia Papagiannaki
        for x in ['ROOTNODE', 'SERIAL', 'NODE', 'HASH', 'SIZE', 'TYPE',
279 adce84cd Sofia Papagiannaki
                  'MTIME', 'MUSER', 'UUID', 'CHECKSUM', 'CLUSTER',
280 adce84cd Sofia Papagiannaki
                  'MATCH_PREFIX', 'MATCH_EXACT']:
281 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
282 2715ade4 Sofia Papagiannaki
283 ace7592b Sofia Papagiannaki
        self.ALLOWED = ['read', 'write']
284 dc88754b Nanakos Chrysostomos
285 46286f5f Antony Chazapis
        self.block_module = load_module(block_module)
286 7f1f0464 Georgios D. Tsoukalas
        self.block_params = block_params
287 7ca7bb08 Antony Chazapis
        params = {'path': block_path,
288 7ca7bb08 Antony Chazapis
                  'block_size': self.block_size,
289 f3b65e8f Antony Chazapis
                  'hash_algorithm': self.hash_algorithm,
290 f3b65e8f Antony Chazapis
                  'umask': block_umask}
291 7f1f0464 Georgios D. Tsoukalas
        params.update(self.block_params)
292 7ca7bb08 Antony Chazapis
        self.store = self.block_module.Store(**params)
293 46286f5f Antony Chazapis
294 f4fbb0fa Sofia Papagiannaki
        if queue_module and queue_hosts:
295 46286f5f Antony Chazapis
            self.queue_module = load_module(queue_module)
296 f4fbb0fa Sofia Papagiannaki
            params = {'hosts': queue_hosts,
297 7f1f0464 Georgios D. Tsoukalas
                      'exchange': queue_exchange,
298 fa9cae7e Antony Chazapis
                      'client_id': QUEUE_CLIENT_ID}
299 46286f5f Antony Chazapis
            self.queue = self.queue_module.Queue(**params)
300 46286f5f Antony Chazapis
        else:
301 46286f5f Antony Chazapis
            class NoQueue:
302 1a239dc1 Sofia Papagiannaki
                def send(self, *args):
303 46286f5f Antony Chazapis
                    pass
304 2715ade4 Sofia Papagiannaki
305 b9a8feec root
                def close(self):
306 b9a8feec root
                    pass
307 2715ade4 Sofia Papagiannaki
308 46286f5f Antony Chazapis
            self.queue = NoQueue()
309 2715ade4 Sofia Papagiannaki
310 f759041f Ilias Tsitsimpis
        self.astakos_auth_url = astakos_auth_url
311 16f2673e Sofia Papagiannaki
        self.service_token = service_token
312 7ee27246 Georgios D. Tsoukalas
313 f759041f Ilias Tsitsimpis
        if not astakos_auth_url or not AstakosClient:
314 7ee27246 Georgios D. Tsoukalas
            self.astakosclient = DisabledAstakosClient(
315 f759041f Ilias Tsitsimpis
                service_token, astakos_auth_url,
316 7ee27246 Georgios D. Tsoukalas
                use_pool=True,
317 7ee27246 Georgios D. Tsoukalas
                pool_size=astakosclient_poolsize)
318 7ee27246 Georgios D. Tsoukalas
        else:
319 7ee27246 Georgios D. Tsoukalas
            self.astakosclient = AstakosClient(
320 f759041f Ilias Tsitsimpis
                service_token, astakos_auth_url,
321 7ee27246 Georgios D. Tsoukalas
                use_pool=True,
322 7ee27246 Georgios D. Tsoukalas
                pool_size=astakosclient_poolsize)
323 b336e6fa Georgios D. Tsoukalas
324 7ed99da8 root
        self.serials = []
325 a7f7699d root
        self.messages = []
326 0307b47f Georgios D. Tsoukalas
327 9e3a38bb Sofia Papagiannaki
        self._move_object = partial(self._copy_object, is_move=True)
328 9e3a38bb Sofia Papagiannaki
329 f6d499b0 Sofia Papagiannaki
        self.lock_container_path = False
330 f6d499b0 Sofia Papagiannaki
331 5d022141 Sofia Papagiannaki
        self.in_transaction = False
332 5d022141 Sofia Papagiannaki
333 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
334 ebdbac7a Sofia Papagiannaki
335 d47565d8 Buildbot
    def pre_exec(self, lock_container_path=False):
336 d47565d8 Buildbot
        self.lock_container_path = lock_container_path
337 cc62d2ab Sofia Papagiannaki
        self.wrapper.execute()
338 5d022141 Sofia Papagiannaki
        self.serials = []
339 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
340 5d022141 Sofia Papagiannaki
        self.in_transaction = True
341 cc62d2ab Sofia Papagiannaki
342 d47565d8 Buildbot
    def post_exec(self, success_status=True):
343 cc62d2ab Sofia Papagiannaki
        if success_status:
344 cc62d2ab Sofia Papagiannaki
            # send messages produced
345 cc62d2ab Sofia Papagiannaki
            for m in self.messages:
346 cc62d2ab Sofia Papagiannaki
                self.queue.send(*m)
347 cc62d2ab Sofia Papagiannaki
348 cc62d2ab Sofia Papagiannaki
            # register serials
349 cc62d2ab Sofia Papagiannaki
            if self.serials:
350 cc62d2ab Sofia Papagiannaki
                self.commission_serials.insert_many(
351 cc62d2ab Sofia Papagiannaki
                    self.serials)
352 cc62d2ab Sofia Papagiannaki
353 cc62d2ab Sofia Papagiannaki
                # commit to ensure that the serials are registered
354 cc62d2ab Sofia Papagiannaki
                # even if resolve commission fails
355 cc62d2ab Sofia Papagiannaki
                self.wrapper.commit()
356 cc62d2ab Sofia Papagiannaki
357 cc62d2ab Sofia Papagiannaki
                # start new transaction
358 cc62d2ab Sofia Papagiannaki
                self.wrapper.execute()
359 cc62d2ab Sofia Papagiannaki
360 cc62d2ab Sofia Papagiannaki
                r = self.astakosclient.resolve_commissions(
361 29148653 Sofia Papagiannaki
                    accept_serials=self.serials,
362 29148653 Sofia Papagiannaki
                    reject_serials=[])
363 cc62d2ab Sofia Papagiannaki
                self.commission_serials.delete_many(
364 cc62d2ab Sofia Papagiannaki
                    r['accepted'])
365 cc62d2ab Sofia Papagiannaki
366 cc62d2ab Sofia Papagiannaki
            self.wrapper.commit()
367 cc62d2ab Sofia Papagiannaki
        else:
368 cc62d2ab Sofia Papagiannaki
            if self.serials:
369 135f864e Sofia Papagiannaki
                r = self.astakosclient.resolve_commissions(
370 cc62d2ab Sofia Papagiannaki
                    accept_serials=[],
371 cc62d2ab Sofia Papagiannaki
                    reject_serials=self.serials)
372 96f9f6fd Sofia Papagiannaki
                self.commission_serials.delete_many(
373 96f9f6fd Sofia Papagiannaki
                    r['rejected'])
374 cc62d2ab Sofia Papagiannaki
            self.wrapper.rollback()
375 5d022141 Sofia Papagiannaki
        self.in_transaction = False
376 cc62d2ab Sofia Papagiannaki
377 d14fe290 Antony Chazapis
    def close(self):
378 d14fe290 Antony Chazapis
        self.wrapper.close()
379 b9a8feec root
        self.queue.close()
380 2715ade4 Sofia Papagiannaki
381 c846fad1 Sofia Papagiannaki
    @property
382 c846fad1 Sofia Papagiannaki
    def using_external_quotaholder(self):
383 7ee27246 Georgios D. Tsoukalas
        return not isinstance(self.astakosclient, DisabledAstakosClient)
384 c846fad1 Sofia Papagiannaki
385 1f96b68d Sofia Papagiannaki
    @debug_method
386 5d022141 Sofia Papagiannaki
    @backend_method
387 e5b77cde Sofia Papagiannaki
    @list_method
388 a9b3f29d Antony Chazapis
    def list_accounts(self, user, marker=None, limit=10000):
389 a9b3f29d Antony Chazapis
        """Return a list of accounts the user can access."""
390 2715ade4 Sofia Papagiannaki
391 e5b77cde Sofia Papagiannaki
        return self._allowed_accounts(user)
392 2715ade4 Sofia Papagiannaki
393 3b8f938b Sofia Papagiannaki
    def _get_account_quotas(self, account):
394 3b8f938b Sofia Papagiannaki
        """Get account usage from astakos."""
395 3b8f938b Sofia Papagiannaki
396 3b8f938b Sofia Papagiannaki
        quotas = self.astakosclient.service_get_quotas(account)[account]
397 3b8f938b Sofia Papagiannaki
        return quotas.get(DEFAULT_SOURCE, {}).get(DEFAULT_DISKSPACE_RESOURCE,
398 3b8f938b Sofia Papagiannaki
                                                  {})
399 3b8f938b Sofia Papagiannaki
400 ec6f741b Sofia Papagiannaki
    def _get_account_quotas(self, account):
401 ec6f741b Sofia Papagiannaki
        """Get account usage from astakos."""
402 ec6f741b Sofia Papagiannaki
403 ec6f741b Sofia Papagiannaki
        quotas = self.astakosclient.service_get_quotas(account)[account]
404 ec6f741b Sofia Papagiannaki
        return quotas.get(DEFAULT_SOURCE, {}).get(DEFAULT_DISKSPACE_RESOURCE,
405 ec6f741b Sofia Papagiannaki
                                                  {})
406 ec6f741b Sofia Papagiannaki
407 1f96b68d Sofia Papagiannaki
    @debug_method
408 5d022141 Sofia Papagiannaki
    @backend_method
409 3b8f938b Sofia Papagiannaki
    def get_account_meta(self, user, account, domain, until=None,
410 3b8f938b Sofia Papagiannaki
                         include_user_defined=True):
411 cb69c154 Antony Chazapis
        """Return a dictionary with the account metadata for the domain."""
412 2715ade4 Sofia Papagiannaki
413 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
414 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, user == account)
415 a9b3f29d Antony Chazapis
        if user != account:
416 ebdbac7a Sofia Papagiannaki
            if until or (node is None):
417 a9b3f29d Antony Chazapis
                raise NotAllowedError
418 a9b3f29d Antony Chazapis
        try:
419 44ad5860 Antony Chazapis
            props = self._get_properties(node, until)
420 2c5363a0 Antony Chazapis
            mtime = props[self.MTIME]
421 a9b3f29d Antony Chazapis
        except NameError:
422 62f915a1 Antony Chazapis
            props = None
423 a9b3f29d Antony Chazapis
            mtime = until
424 2e8edd42 Sofia Papagiannaki
        count, bytes, tstamp = self._get_statistics(node, until, compute=True)
425 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
426 a9b3f29d Antony Chazapis
        if until is None:
427 a9b3f29d Antony Chazapis
            modified = tstamp
428 a9b3f29d Antony Chazapis
        else:
429 2715ade4 Sofia Papagiannaki
            modified = self._get_statistics(
430 2e8edd42 Sofia Papagiannaki
                node, compute=True)[2]  # Overall last modification.
431 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
432 2715ade4 Sofia Papagiannaki
433 a9b3f29d Antony Chazapis
        if user != account:
434 a9b3f29d Antony Chazapis
            meta = {'name': account}
435 a9b3f29d Antony Chazapis
        else:
436 44ad5860 Antony Chazapis
            meta = {}
437 82482e2c Antony Chazapis
            if props is not None and include_user_defined:
438 2715ade4 Sofia Papagiannaki
                meta.update(
439 2715ade4 Sofia Papagiannaki
                    dict(self.node.attribute_get(props[self.SERIAL], domain)))
440 a9b3f29d Antony Chazapis
            if until is not None:
441 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
442 44ad5860 Antony Chazapis
            meta.update({'name': account, 'count': count, 'bytes': bytes})
443 0ed09c7c Sofia Papagiannaki
            if self.using_external_quotaholder:
444 3b8f938b Sofia Papagiannaki
                external_quota = self._get_account_quotas(account)
445 02de6286 Sofia Papagiannaki
                meta['bytes'] = external_quota.get('usage', 0)
446 a9b3f29d Antony Chazapis
        meta.update({'modified': modified})
447 a9b3f29d Antony Chazapis
        return meta
448 2715ade4 Sofia Papagiannaki
449 1f96b68d Sofia Papagiannaki
    @debug_method
450 5d022141 Sofia Papagiannaki
    @backend_method
451 cb69c154 Antony Chazapis
    def update_account_meta(self, user, account, domain, meta, replace=False):
452 cb69c154 Antony Chazapis
        """Update the metadata associated with the account for the domain."""
453 2715ade4 Sofia Papagiannaki
454 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
455 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, True)
456 0f510652 Sofia Papagiannaki
        self._put_metadata(user, node, domain, meta, replace,
457 0f510652 Sofia Papagiannaki
                           update_statistics_ancestors_depth=-1)
458 2715ade4 Sofia Papagiannaki
459 1f96b68d Sofia Papagiannaki
    @debug_method
460 5d022141 Sofia Papagiannaki
    @backend_method
461 a9b3f29d Antony Chazapis
    def get_account_groups(self, user, account):
462 29148653 Sofia Papagiannaki
        """Return a dictionary with the user groups defined for the account."""
463 2715ade4 Sofia Papagiannaki
464 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
465 a9b3f29d Antony Chazapis
        if user != account:
466 a9b3f29d Antony Chazapis
            return {}
467 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
468 0f9d752c Antony Chazapis
        return self.permissions.group_dict(account)
469 2715ade4 Sofia Papagiannaki
470 1f96b68d Sofia Papagiannaki
    @debug_method
471 5d022141 Sofia Papagiannaki
    @backend_method
472 a9b3f29d Antony Chazapis
    def update_account_groups(self, user, account, groups, replace=False):
473 a9b3f29d Antony Chazapis
        """Update the groups associated with the account."""
474 2715ade4 Sofia Papagiannaki
475 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
476 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
477 a9b3f29d Antony Chazapis
        self._check_groups(groups)
478 0f9d752c Antony Chazapis
        if replace:
479 0f9d752c Antony Chazapis
            self.permissions.group_destroy(account)
480 0f9d752c Antony Chazapis
        for k, v in groups.iteritems():
481 2715ade4 Sofia Papagiannaki
            if not replace:  # If not already deleted.
482 0f9d752c Antony Chazapis
                self.permissions.group_delete(account, k)
483 0f9d752c Antony Chazapis
            if v:
484 0f9d752c Antony Chazapis
                self.permissions.group_addmany(account, k, v)
485 2715ade4 Sofia Papagiannaki
486 1f96b68d Sofia Papagiannaki
    @debug_method
487 5d022141 Sofia Papagiannaki
    @backend_method
488 3b8f938b Sofia Papagiannaki
    def get_account_policy(self, user, account):
489 b2832c6a Antony Chazapis
        """Return a dictionary with the account policy."""
490 2715ade4 Sofia Papagiannaki
491 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
492 b2832c6a Antony Chazapis
        if user != account:
493 647a5f48 Antony Chazapis
            return {}
494 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
495 19ddd41b Sofia Papagiannaki
        policy = self._get_policy(node, is_account_policy=True)
496 07fcdb96 Sofia Papagiannaki
        if self.using_external_quotaholder:
497 3b8f938b Sofia Papagiannaki
            external_quota = self._get_account_quotas(account)
498 02de6286 Sofia Papagiannaki
            policy['quota'] = external_quota.get('limit', 0)
499 c846fad1 Sofia Papagiannaki
        return policy
500 2715ade4 Sofia Papagiannaki
501 1f96b68d Sofia Papagiannaki
    @debug_method
502 5d022141 Sofia Papagiannaki
    @backend_method
503 b2832c6a Antony Chazapis
    def update_account_policy(self, user, account, policy, replace=False):
504 b2832c6a Antony Chazapis
        """Update the policy associated with the account."""
505 2715ade4 Sofia Papagiannaki
506 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
507 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
508 19ddd41b Sofia Papagiannaki
        self._check_policy(policy, is_account_policy=True)
509 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, replace, is_account_policy=True)
510 2715ade4 Sofia Papagiannaki
511 1f96b68d Sofia Papagiannaki
    @debug_method
512 5d022141 Sofia Papagiannaki
    @backend_method
513 78348987 Sofia Papagiannaki
    def put_account(self, user, account, policy=None):
514 a9b3f29d Antony Chazapis
        """Create a new account with the given name."""
515 2715ade4 Sofia Papagiannaki
516 78348987 Sofia Papagiannaki
        policy = policy or {}
517 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
518 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
519 44ad5860 Antony Chazapis
        if node is not None:
520 7efc9f86 Sofia Papagiannaki
            raise AccountExists('Account already exists')
521 b2832c6a Antony Chazapis
        if policy:
522 19ddd41b Sofia Papagiannaki
            self._check_policy(policy, is_account_policy=True)
523 0f510652 Sofia Papagiannaki
        node = self._put_path(user, self.ROOTNODE, account,
524 0f510652 Sofia Papagiannaki
                              update_statistics_ancestors_depth=-1)
525 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, True, is_account_policy=True)
526 2715ade4 Sofia Papagiannaki
527 1f96b68d Sofia Papagiannaki
    @debug_method
528 5d022141 Sofia Papagiannaki
    @backend_method
529 a9b3f29d Antony Chazapis
    def delete_account(self, user, account):
530 a9b3f29d Antony Chazapis
        """Delete the account with the given name."""
531 2715ade4 Sofia Papagiannaki
532 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
533 c915d3bf Antony Chazapis
        node = self.node.node_lookup(account)
534 c915d3bf Antony Chazapis
        if node is None:
535 c915d3bf Antony Chazapis
            return
536 0f510652 Sofia Papagiannaki
        if not self.node.node_remove(node,
537 0f510652 Sofia Papagiannaki
                                     update_statistics_ancestors_depth=-1):
538 7efc9f86 Sofia Papagiannaki
            raise AccountNotEmpty('Account is not empty')
539 0f9d752c Antony Chazapis
        self.permissions.group_destroy(account)
540 2715ade4 Sofia Papagiannaki
541 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
542 ebdbac7a Sofia Papagiannaki
        # removing the specific path could be more expensive
543 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
544 ebdbac7a Sofia Papagiannaki
545 1f96b68d Sofia Papagiannaki
    @debug_method
546 5d022141 Sofia Papagiannaki
    @backend_method
547 e5b77cde Sofia Papagiannaki
    @list_method
548 29148653 Sofia Papagiannaki
    def list_containers(self, user, account, marker=None, limit=10000,
549 29148653 Sofia Papagiannaki
                        shared=False, until=None, public=False):
550 62f915a1 Antony Chazapis
        """Return a list of containers existing under an account."""
551 2715ade4 Sofia Papagiannaki
552 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
553 62f915a1 Antony Chazapis
        if user != account:
554 ebdbac7a Sofia Papagiannaki
            if until:
555 62f915a1 Antony Chazapis
                raise NotAllowedError
556 e5b77cde Sofia Papagiannaki
            return self._allowed_containers(user, account)
557 90ee1eb3 Sofia Papagiannaki
        if shared or public:
558 56ac7c81 Sofia Papagiannaki
            allowed = set()
559 90ee1eb3 Sofia Papagiannaki
            if shared:
560 29148653 Sofia Papagiannaki
                allowed.update([x.split('/', 2)[1] for x in
561 29148653 Sofia Papagiannaki
                               self.permissions.access_list_shared(account)])
562 90ee1eb3 Sofia Papagiannaki
            if public:
563 29148653 Sofia Papagiannaki
                allowed.update([x[0].split('/', 2)[1] for x in
564 29148653 Sofia Papagiannaki
                               self.permissions.public_list(account)])
565 e5b77cde Sofia Papagiannaki
            return sorted(allowed)
566 62f915a1 Antony Chazapis
        node = self.node.node_lookup(account)
567 e5b77cde Sofia Papagiannaki
        return [x[0] for x in self._list_object_properties(
568 2715ade4 Sofia Papagiannaki
            node, account, '', '/', marker, limit, False, None, [], until)]
569 2715ade4 Sofia Papagiannaki
570 1f96b68d Sofia Papagiannaki
    @debug_method
571 5d022141 Sofia Papagiannaki
    @backend_method
572 29148653 Sofia Papagiannaki
    def list_container_meta(self, user, account, container, domain,
573 29148653 Sofia Papagiannaki
                            until=None):
574 29148653 Sofia Papagiannaki
        """Return a list of the container's object meta keys for a domain."""
575 2715ade4 Sofia Papagiannaki
576 ebdbac7a Sofia Papagiannaki
        self._can_read_container(user, account, container)
577 371d907a Antony Chazapis
        allowed = []
578 371d907a Antony Chazapis
        if user != account:
579 371d907a Antony Chazapis
            if until:
580 371d907a Antony Chazapis
                raise NotAllowedError
581 371d907a Antony Chazapis
        path, node = self._lookup_container(account, container)
582 371d907a Antony Chazapis
        before = until if until is not None else inf
583 371d907a Antony Chazapis
        allowed = self._get_formatted_paths(allowed)
584 29148653 Sofia Papagiannaki
        return self.node.latest_attribute_keys(node, domain, before,
585 29148653 Sofia Papagiannaki
                                               CLUSTER_DELETED, allowed)
586 2715ade4 Sofia Papagiannaki
587 1f96b68d Sofia Papagiannaki
    @debug_method
588 5d022141 Sofia Papagiannaki
    @backend_method
589 29148653 Sofia Papagiannaki
    def get_container_meta(self, user, account, container, domain, until=None,
590 29148653 Sofia Papagiannaki
                           include_user_defined=True):
591 cb69c154 Antony Chazapis
        """Return a dictionary with the container metadata for the domain."""
592 2715ade4 Sofia Papagiannaki
593 ebdbac7a Sofia Papagiannaki
        self._can_read_container(user, account, container)
594 a9b3f29d Antony Chazapis
        if user != account:
595 ebdbac7a Sofia Papagiannaki
            if until:
596 a9b3f29d Antony Chazapis
                raise NotAllowedError
597 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
598 c915d3bf Antony Chazapis
        props = self._get_properties(node, until)
599 2c5363a0 Antony Chazapis
        mtime = props[self.MTIME]
600 62f915a1 Antony Chazapis
        count, bytes, tstamp = self._get_statistics(node, until)
601 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
602 a9b3f29d Antony Chazapis
        if until is None:
603 a9b3f29d Antony Chazapis
            modified = tstamp
604 a9b3f29d Antony Chazapis
        else:
605 2715ade4 Sofia Papagiannaki
            modified = self._get_statistics(
606 2715ade4 Sofia Papagiannaki
                node)[2]  # Overall last modification.
607 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
608 2715ade4 Sofia Papagiannaki
609 a9b3f29d Antony Chazapis
        if user != account:
610 c915d3bf Antony Chazapis
            meta = {'name': container}
611 a9b3f29d Antony Chazapis
        else:
612 82482e2c Antony Chazapis
            meta = {}
613 82482e2c Antony Chazapis
            if include_user_defined:
614 2715ade4 Sofia Papagiannaki
                meta.update(
615 2715ade4 Sofia Papagiannaki
                    dict(self.node.attribute_get(props[self.SERIAL], domain)))
616 a9b3f29d Antony Chazapis
            if until is not None:
617 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
618 c915d3bf Antony Chazapis
            meta.update({'name': container, 'count': count, 'bytes': bytes})
619 c915d3bf Antony Chazapis
        meta.update({'modified': modified})
620 a9b3f29d Antony Chazapis
        return meta
621 2715ade4 Sofia Papagiannaki
622 1f96b68d Sofia Papagiannaki
    @debug_method
623 5d022141 Sofia Papagiannaki
    @backend_method
624 29148653 Sofia Papagiannaki
    def update_container_meta(self, user, account, container, domain, meta,
625 29148653 Sofia Papagiannaki
                              replace=False):
626 cb69c154 Antony Chazapis
        """Update the metadata associated with the container for the domain."""
627 2715ade4 Sofia Papagiannaki
628 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
629 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
630 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
631 0f510652 Sofia Papagiannaki
            user, node, domain, meta, replace,
632 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=0)
633 f9ea264b Antony Chazapis
        if src_version_id is not None:
634 19ddd41b Sofia Papagiannaki
            versioning = self._get_policy(
635 19ddd41b Sofia Papagiannaki
                node, is_account_policy=False)['versioning']
636 f9ea264b Antony Chazapis
            if versioning != 'auto':
637 0f510652 Sofia Papagiannaki
                self.node.version_remove(src_version_id,
638 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth=0)
639 2715ade4 Sofia Papagiannaki
640 1f96b68d Sofia Papagiannaki
    @debug_method
641 5d022141 Sofia Papagiannaki
    @backend_method
642 a9b3f29d Antony Chazapis
    def get_container_policy(self, user, account, container):
643 a9b3f29d Antony Chazapis
        """Return a dictionary with the container policy."""
644 2715ade4 Sofia Papagiannaki
645 ebdbac7a Sofia Papagiannaki
        self._can_read_container(user, account, container)
646 a9b3f29d Antony Chazapis
        if user != account:
647 a9b3f29d Antony Chazapis
            return {}
648 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
649 19ddd41b Sofia Papagiannaki
        return self._get_policy(node, is_account_policy=False)
650 2715ade4 Sofia Papagiannaki
651 1f96b68d Sofia Papagiannaki
    @debug_method
652 5d022141 Sofia Papagiannaki
    @backend_method
653 29148653 Sofia Papagiannaki
    def update_container_policy(self, user, account, container, policy,
654 29148653 Sofia Papagiannaki
                                replace=False):
655 b2832c6a Antony Chazapis
        """Update the policy associated with the container."""
656 2715ade4 Sofia Papagiannaki
657 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
658 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
659 19ddd41b Sofia Papagiannaki
        self._check_policy(policy, is_account_policy=False)
660 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, replace, is_account_policy=False)
661 2715ade4 Sofia Papagiannaki
662 1f96b68d Sofia Papagiannaki
    @debug_method
663 5d022141 Sofia Papagiannaki
    @backend_method
664 78348987 Sofia Papagiannaki
    def put_container(self, user, account, container, policy=None):
665 a9b3f29d Antony Chazapis
        """Create a new container with the given name."""
666 2715ade4 Sofia Papagiannaki
667 78348987 Sofia Papagiannaki
        policy = policy or {}
668 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
669 a9b3f29d Antony Chazapis
        try:
670 c915d3bf Antony Chazapis
            path, node = self._lookup_container(account, container)
671 a9b3f29d Antony Chazapis
        except NameError:
672 a9b3f29d Antony Chazapis
            pass
673 a9b3f29d Antony Chazapis
        else:
674 7efc9f86 Sofia Papagiannaki
            raise ContainerExists('Container already exists')
675 a9b3f29d Antony Chazapis
        if policy:
676 19ddd41b Sofia Papagiannaki
            self._check_policy(policy, is_account_policy=False)
677 a9b3f29d Antony Chazapis
        path = '/'.join((account, container))
678 2715ade4 Sofia Papagiannaki
        node = self._put_path(
679 0f510652 Sofia Papagiannaki
            user, self._lookup_account(account, True)[1], path,
680 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=-1)
681 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, True, is_account_policy=False)
682 2715ade4 Sofia Papagiannaki
683 1f96b68d Sofia Papagiannaki
    @debug_method
684 5d022141 Sofia Papagiannaki
    @backend_method
685 29148653 Sofia Papagiannaki
    def delete_container(self, user, account, container, until=None, prefix='',
686 29148653 Sofia Papagiannaki
                         delimiter=None):
687 a9b3f29d Antony Chazapis
        """Delete/purge the container with the given name."""
688 2715ade4 Sofia Papagiannaki
689 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
690 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
691 2715ade4 Sofia Papagiannaki
692 a9b3f29d Antony Chazapis
        if until is not None:
693 388ea25f Sofia Papagiannaki
            hashes, size, serials = self.node.node_purge_children(
694 0f510652 Sofia Papagiannaki
                node, until, CLUSTER_HISTORY,
695 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=0)
696 04230536 Antony Chazapis
            for h in hashes:
697 04230536 Antony Chazapis
                self.store.map_delete(h)
698 0f510652 Sofia Papagiannaki
            self.node.node_purge_children(node, until, CLUSTER_DELETED,
699 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=0)
700 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
701 0a92ff85 Sofia Papagiannaki
                self._report_size_change(
702 0a92ff85 Sofia Papagiannaki
                    user, account, -size, {
703 29148653 Sofia Papagiannaki
                        'action': 'container purge',
704 0a92ff85 Sofia Papagiannaki
                        'path': path,
705 0a92ff85 Sofia Papagiannaki
                        'versions': ','.join(str(i) for i in serials)
706 0a92ff85 Sofia Papagiannaki
                    }
707 0a92ff85 Sofia Papagiannaki
                )
708 a9b3f29d Antony Chazapis
            return
709 2715ade4 Sofia Papagiannaki
710 e46b2bcf Sofia Papagiannaki
        if not delimiter:
711 e46b2bcf Sofia Papagiannaki
            if self._get_statistics(node)[0] > 0:
712 e46b2bcf Sofia Papagiannaki
                raise ContainerNotEmpty('Container is not empty')
713 388ea25f Sofia Papagiannaki
            hashes, size, serials = self.node.node_purge_children(
714 0f510652 Sofia Papagiannaki
                node, inf, CLUSTER_HISTORY,
715 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=0)
716 e46b2bcf Sofia Papagiannaki
            for h in hashes:
717 e46b2bcf Sofia Papagiannaki
                self.store.map_delete(h)
718 0f510652 Sofia Papagiannaki
            self.node.node_purge_children(node, inf, CLUSTER_DELETED,
719 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=0)
720 0f510652 Sofia Papagiannaki
            self.node.node_remove(node, update_statistics_ancestors_depth=0)
721 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
722 0a92ff85 Sofia Papagiannaki
                self._report_size_change(
723 0a92ff85 Sofia Papagiannaki
                    user, account, -size, {
724 29148653 Sofia Papagiannaki
                        'action': 'container purge',
725 0a92ff85 Sofia Papagiannaki
                        'path': path,
726 0a92ff85 Sofia Papagiannaki
                        'versions': ','.join(str(i) for i in serials)
727 0a92ff85 Sofia Papagiannaki
                    }
728 0a92ff85 Sofia Papagiannaki
                )
729 e46b2bcf Sofia Papagiannaki
        else:
730 b1dadd0e Sofia Papagiannaki
            # remove only contents
731 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
732 29148653 Sofia Papagiannaki
                user, account, container, prefix='', delimiter=None,
733 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
734 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
735 e46b2bcf Sofia Papagiannaki
            paths = []
736 e46b2bcf Sofia Papagiannaki
            for t in src_names:
737 e46b2bcf Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
738 e46b2bcf Sofia Papagiannaki
                node = t[2]
739 33af031c Sofia Papagiannaki
                if not self._exists(node):
740 33af031c Sofia Papagiannaki
                    continue
741 0f510652 Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(
742 0f510652 Sofia Papagiannaki
                    user, node, size=0, type='', hash=None, checksum='',
743 0f510652 Sofia Papagiannaki
                    cluster=CLUSTER_DELETED,
744 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
745 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
746 0f510652 Sofia Papagiannaki
                    account, container, src_version_id,
747 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
748 b1dadd0e Sofia Papagiannaki
                self._report_size_change(
749 29148653 Sofia Papagiannaki
                    user, account, -del_size, {
750 29148653 Sofia Papagiannaki
                        'action': 'object delete',
751 29148653 Sofia Papagiannaki
                        'path': path,
752 29148653 Sofia Papagiannaki
                        'versions': ','.join([str(dest_version_id)])})
753 2715ade4 Sofia Papagiannaki
                self._report_object_change(
754 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
755 e46b2bcf Sofia Papagiannaki
                paths.append(path)
756 e46b2bcf Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
757 2715ade4 Sofia Papagiannaki
758 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
759 ebdbac7a Sofia Papagiannaki
        # removing the specific path could be more expensive
760 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
761 ebdbac7a Sofia Papagiannaki
762 29148653 Sofia Papagiannaki
    def _list_objects(self, user, account, container, prefix, delimiter,
763 29148653 Sofia Papagiannaki
                      marker, limit, virtual, domain, keys, shared, until,
764 29148653 Sofia Papagiannaki
                      size_range, all_props, public):
765 15a96c3e Antony Chazapis
        if user != account and until:
766 15a96c3e Antony Chazapis
            raise NotAllowedError
767 9c6ddb46 Sofia Papagiannaki
768 9c6ddb46 Sofia Papagiannaki
        objects = []
769 cf4a7a7b Sofia Papagiannaki
        if shared and public:
770 cf4a7a7b Sofia Papagiannaki
            # get shared first
771 5576e6dd Sofia Papagiannaki
            shared_paths = self._list_object_permissions(
772 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, shared=True, public=False)
773 5576e6dd Sofia Papagiannaki
            if shared_paths:
774 cf4a7a7b Sofia Papagiannaki
                path, node = self._lookup_container(account, container)
775 5576e6dd Sofia Papagiannaki
                shared_paths = self._get_formatted_paths(shared_paths)
776 9c6ddb46 Sofia Papagiannaki
                objects = set(self._list_object_properties(
777 29148653 Sofia Papagiannaki
                    node, path, prefix, delimiter, marker, limit, virtual,
778 29148653 Sofia Papagiannaki
                    domain, keys, until, size_range, shared_paths, all_props))
779 2715ade4 Sofia Papagiannaki
780 cf4a7a7b Sofia Papagiannaki
            # get public
781 2715ade4 Sofia Papagiannaki
            objects |= set(self._list_public_object_properties(
782 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, all_props))
783 31e1acd3 Sofia Papagiannaki
            objects = list(objects)
784 2715ade4 Sofia Papagiannaki
785 cf4a7a7b Sofia Papagiannaki
            objects.sort(key=lambda x: x[0])
786 cf4a7a7b Sofia Papagiannaki
        elif public:
787 2715ade4 Sofia Papagiannaki
            objects = self._list_public_object_properties(
788 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, all_props)
789 9c6ddb46 Sofia Papagiannaki
        else:
790 9c6ddb46 Sofia Papagiannaki
            allowed = self._list_object_permissions(
791 9c6ddb46 Sofia Papagiannaki
                user, account, container, prefix, shared, public=False)
792 9c6ddb46 Sofia Papagiannaki
            if shared and not allowed:
793 9c6ddb46 Sofia Papagiannaki
                return []
794 9c6ddb46 Sofia Papagiannaki
            path, node = self._lookup_container(account, container)
795 9c6ddb46 Sofia Papagiannaki
            allowed = self._get_formatted_paths(allowed)
796 9c6ddb46 Sofia Papagiannaki
            objects = self._list_object_properties(
797 9c6ddb46 Sofia Papagiannaki
                node, path, prefix, delimiter, marker, limit, virtual, domain,
798 9c6ddb46 Sofia Papagiannaki
                keys, until, size_range, allowed, all_props)
799 9c6ddb46 Sofia Papagiannaki
800 9c6ddb46 Sofia Papagiannaki
        # apply limits
801 9c6ddb46 Sofia Papagiannaki
        start, limit = self._list_limits(objects, marker, limit)
802 cf4a7a7b Sofia Papagiannaki
        return objects[start:start + limit]
803 2715ade4 Sofia Papagiannaki
804 29148653 Sofia Papagiannaki
    def _list_public_object_properties(self, user, account, container, prefix,
805 29148653 Sofia Papagiannaki
                                       all_props):
806 2715ade4 Sofia Papagiannaki
        public = self._list_object_permissions(
807 2715ade4 Sofia Papagiannaki
            user, account, container, prefix, shared=False, public=True)
808 cf4a7a7b Sofia Papagiannaki
        paths, nodes = self._lookup_objects(public)
809 cf4a7a7b Sofia Papagiannaki
        path = '/'.join((account, container))
810 cf4a7a7b Sofia Papagiannaki
        cont_prefix = path + '/'
811 cf4a7a7b Sofia Papagiannaki
        paths = [x[len(cont_prefix):] for x in paths]
812 29148653 Sofia Papagiannaki
        objects = [(p,) + props for p, props in
813 29148653 Sofia Papagiannaki
                   zip(paths, self.node.version_lookup_bulk(
814 ba402621 Sofia Papagiannaki
                       nodes, all_props=all_props, order_by_path=True))]
815 cf4a7a7b Sofia Papagiannaki
        return objects
816 2715ade4 Sofia Papagiannaki
817 29148653 Sofia Papagiannaki
    def _list_objects_no_limit(self, user, account, container, prefix,
818 29148653 Sofia Papagiannaki
                               delimiter, virtual, domain, keys, shared, until,
819 29148653 Sofia Papagiannaki
                               size_range, all_props, public):
820 4d15c94e Sofia Papagiannaki
        objects = []
821 4d15c94e Sofia Papagiannaki
        while True:
822 4d15c94e Sofia Papagiannaki
            marker = objects[-1] if objects else None
823 4d15c94e Sofia Papagiannaki
            limit = 10000
824 29148653 Sofia Papagiannaki
            l = self._list_objects(
825 29148653 Sofia Papagiannaki
                user, account, container, prefix, delimiter, marker, limit,
826 29148653 Sofia Papagiannaki
                virtual, domain, keys, shared, until, size_range, all_props,
827 29148653 Sofia Papagiannaki
                public)
828 4d15c94e Sofia Papagiannaki
            objects.extend(l)
829 4d15c94e Sofia Papagiannaki
            if not l or len(l) < limit:
830 4d15c94e Sofia Papagiannaki
                break
831 4d15c94e Sofia Papagiannaki
        return objects
832 2715ade4 Sofia Papagiannaki
833 29148653 Sofia Papagiannaki
    def _list_object_permissions(self, user, account, container, prefix,
834 29148653 Sofia Papagiannaki
                                 shared, public):
835 62f915a1 Antony Chazapis
        allowed = []
836 fcd37c40 Antony Chazapis
        path = '/'.join((account, container, prefix)).rstrip('/')
837 62f915a1 Antony Chazapis
        if user != account:
838 fcd37c40 Antony Chazapis
            allowed = self.permissions.access_list_paths(user, path)
839 62f915a1 Antony Chazapis
            if not allowed:
840 62f915a1 Antony Chazapis
                raise NotAllowedError
841 62f915a1 Antony Chazapis
        else:
842 56ac7c81 Sofia Papagiannaki
            allowed = set()
843 62f915a1 Antony Chazapis
            if shared:
844 56ac7c81 Sofia Papagiannaki
                allowed.update(self.permissions.access_list_shared(path))
845 c53c4def Sofia Papagiannaki
            if public:
846 2715ade4 Sofia Papagiannaki
                allowed.update(
847 2715ade4 Sofia Papagiannaki
                    [x[0] for x in self.permissions.public_list(path)])
848 56ac7c81 Sofia Papagiannaki
            allowed = sorted(allowed)
849 c53c4def Sofia Papagiannaki
            if not allowed:
850 c53c4def Sofia Papagiannaki
                return []
851 15a96c3e Antony Chazapis
        return allowed
852 2715ade4 Sofia Papagiannaki
853 1f96b68d Sofia Papagiannaki
    @debug_method
854 5d022141 Sofia Papagiannaki
    @backend_method
855 29148653 Sofia Papagiannaki
    def list_objects(self, user, account, container, prefix='', delimiter=None,
856 29148653 Sofia Papagiannaki
                     marker=None, limit=10000, virtual=True, domain=None,
857 29148653 Sofia Papagiannaki
                     keys=None, shared=False, until=None, size_range=None,
858 29148653 Sofia Papagiannaki
                     public=False):
859 29148653 Sofia Papagiannaki
        """List (object name, object version_id) under a container."""
860 2715ade4 Sofia Papagiannaki
861 78348987 Sofia Papagiannaki
        keys = keys or []
862 29148653 Sofia Papagiannaki
        return self._list_objects(
863 29148653 Sofia Papagiannaki
            user, account, container, prefix, delimiter, marker, limit,
864 29148653 Sofia Papagiannaki
            virtual, domain, keys, shared, until, size_range, False, public)
865 2715ade4 Sofia Papagiannaki
866 1f96b68d Sofia Papagiannaki
    @debug_method
867 5d022141 Sofia Papagiannaki
    @backend_method
868 29148653 Sofia Papagiannaki
    def list_object_meta(self, user, account, container, prefix='',
869 29148653 Sofia Papagiannaki
                         delimiter=None, marker=None, limit=10000,
870 29148653 Sofia Papagiannaki
                         virtual=True, domain=None, keys=None, shared=False,
871 29148653 Sofia Papagiannaki
                         until=None, size_range=None, public=False):
872 29148653 Sofia Papagiannaki
        """Return a list of metadata dicts of objects under a container."""
873 2715ade4 Sofia Papagiannaki
874 78348987 Sofia Papagiannaki
        keys = keys or []
875 29148653 Sofia Papagiannaki
        props = self._list_objects(
876 29148653 Sofia Papagiannaki
            user, account, container, prefix, delimiter, marker, limit,
877 29148653 Sofia Papagiannaki
            virtual, domain, keys, shared, until, size_range, True, public)
878 371d907a Antony Chazapis
        objects = []
879 371d907a Antony Chazapis
        for p in props:
880 371d907a Antony Chazapis
            if len(p) == 2:
881 371d907a Antony Chazapis
                objects.append({'subdir': p[0]})
882 371d907a Antony Chazapis
            else:
883 29148653 Sofia Papagiannaki
                objects.append({
884 29148653 Sofia Papagiannaki
                    'name': p[0],
885 29148653 Sofia Papagiannaki
                    'bytes': p[self.SIZE + 1],
886 29148653 Sofia Papagiannaki
                    'type': p[self.TYPE + 1],
887 29148653 Sofia Papagiannaki
                    'hash': p[self.HASH + 1],
888 29148653 Sofia Papagiannaki
                    'version': p[self.SERIAL + 1],
889 29148653 Sofia Papagiannaki
                    'version_timestamp': p[self.MTIME + 1],
890 29148653 Sofia Papagiannaki
                    'modified': p[self.MTIME + 1] if until is None else None,
891 29148653 Sofia Papagiannaki
                    'modified_by': p[self.MUSER + 1],
892 29148653 Sofia Papagiannaki
                    'uuid': p[self.UUID + 1],
893 29148653 Sofia Papagiannaki
                    'checksum': p[self.CHECKSUM + 1]})
894 371d907a Antony Chazapis
        return objects
895 2715ade4 Sofia Papagiannaki
896 1f96b68d Sofia Papagiannaki
    @debug_method
897 5d022141 Sofia Papagiannaki
    @backend_method
898 15a96c3e Antony Chazapis
    def list_object_permissions(self, user, account, container, prefix=''):
899 29148653 Sofia Papagiannaki
        """Return a list of paths enforce permissions under a container."""
900 2715ade4 Sofia Papagiannaki
901 29148653 Sofia Papagiannaki
        return self._list_object_permissions(user, account, container, prefix,
902 29148653 Sofia Papagiannaki
                                             True, False)
903 2715ade4 Sofia Papagiannaki
904 1f96b68d Sofia Papagiannaki
    @debug_method
905 5d022141 Sofia Papagiannaki
    @backend_method
906 15a96c3e Antony Chazapis
    def list_object_public(self, user, account, container, prefix=''):
907 29148653 Sofia Papagiannaki
        """Return a mapping of object paths to public ids under a container."""
908 2715ade4 Sofia Papagiannaki
909 15a96c3e Antony Chazapis
        public = {}
910 29148653 Sofia Papagiannaki
        for path, p in self.permissions.public_list('/'.join((account,
911 29148653 Sofia Papagiannaki
                                                              container,
912 29148653 Sofia Papagiannaki
                                                              prefix))):
913 56f3c759 Sofia Papagiannaki
            public[path] = p
914 15a96c3e Antony Chazapis
        return public
915 2715ade4 Sofia Papagiannaki
916 1f96b68d Sofia Papagiannaki
    @debug_method
917 5d022141 Sofia Papagiannaki
    @backend_method
918 29148653 Sofia Papagiannaki
    def get_object_meta(self, user, account, container, name, domain,
919 29148653 Sofia Papagiannaki
                        version=None, include_user_defined=True):
920 cb69c154 Antony Chazapis
        """Return a dictionary with the object metadata for the domain."""
921 2715ade4 Sofia Papagiannaki
922 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
923 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
924 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
925 a9b3f29d Antony Chazapis
        if version is None:
926 2c5363a0 Antony Chazapis
            modified = props[self.MTIME]
927 a9b3f29d Antony Chazapis
        else:
928 97d45f69 Antony Chazapis
            try:
929 2715ade4 Sofia Papagiannaki
                modified = self._get_version(
930 2715ade4 Sofia Papagiannaki
                    node)[self.MTIME]  # Overall last modification.
931 2715ade4 Sofia Papagiannaki
            except NameError:  # Object may be deleted.
932 2715ade4 Sofia Papagiannaki
                del_props = self.node.version_lookup(
933 2715ade4 Sofia Papagiannaki
                    node, inf, CLUSTER_DELETED)
934 97d45f69 Antony Chazapis
                if del_props is None:
935 7efc9f86 Sofia Papagiannaki
                    raise ItemNotExists('Object does not exist')
936 97d45f69 Antony Chazapis
                modified = del_props[self.MTIME]
937 2715ade4 Sofia Papagiannaki
938 82482e2c Antony Chazapis
        meta = {}
939 82482e2c Antony Chazapis
        if include_user_defined:
940 2715ade4 Sofia Papagiannaki
            meta.update(
941 2715ade4 Sofia Papagiannaki
                dict(self.node.attribute_get(props[self.SERIAL], domain)))
942 33b4e4a6 Antony Chazapis
        meta.update({'name': name,
943 33b4e4a6 Antony Chazapis
                     'bytes': props[self.SIZE],
944 33b4e4a6 Antony Chazapis
                     'type': props[self.TYPE],
945 371d907a Antony Chazapis
                     'hash': props[self.HASH],
946 33b4e4a6 Antony Chazapis
                     'version': props[self.SERIAL],
947 33b4e4a6 Antony Chazapis
                     'version_timestamp': props[self.MTIME],
948 33b4e4a6 Antony Chazapis
                     'modified': modified,
949 33b4e4a6 Antony Chazapis
                     'modified_by': props[self.MUSER],
950 33b4e4a6 Antony Chazapis
                     'uuid': props[self.UUID],
951 33b4e4a6 Antony Chazapis
                     'checksum': props[self.CHECKSUM]})
952 a9b3f29d Antony Chazapis
        return meta
953 2715ade4 Sofia Papagiannaki
954 1f96b68d Sofia Papagiannaki
    @debug_method
955 5d022141 Sofia Papagiannaki
    @backend_method
956 29148653 Sofia Papagiannaki
    def update_object_meta(self, user, account, container, name, domain, meta,
957 29148653 Sofia Papagiannaki
                           replace=False):
958 29148653 Sofia Papagiannaki
        """Update object metadata for a domain and return the new version."""
959 2715ade4 Sofia Papagiannaki
960 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
961 48bb9c89 Sofia Papagiannaki
962 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
963 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
964 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
965 0f510652 Sofia Papagiannaki
            user, node, domain, meta, replace,
966 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=1)
967 0f510652 Sofia Papagiannaki
        self._apply_versioning(account, container, src_version_id,
968 0f510652 Sofia Papagiannaki
                               update_statistics_ancestors_depth=1)
969 5cc484e1 Antony Chazapis
        return dest_version_id
970 62e6d12e Nanakos Chrysostomos
971 dc88754b Nanakos Chrysostomos
    @debug_method
972 5d022141 Sofia Papagiannaki
    @backend_method
973 dc88754b Nanakos Chrysostomos
    def get_object_permissions_bulk(self, user, account, container, names):
974 dc88754b Nanakos Chrysostomos
        """Return the action allowed on the object, the path
975 dc88754b Nanakos Chrysostomos
        from which the object gets its permissions from,
976 dc88754b Nanakos Chrysostomos
        along with a dictionary containing the permissions."""
977 dc88754b Nanakos Chrysostomos
978 ace7592b Sofia Papagiannaki
        permissions_path = self._get_permissions_path_bulk(account, container,
979 ace7592b Sofia Papagiannaki
                                                           names)
980 dc88754b Nanakos Chrysostomos
        access_objects = self.permissions.access_check_bulk(permissions_path,
981 ace7592b Sofia Papagiannaki
                                                            user)
982 62e6d12e Nanakos Chrysostomos
        #group_parents = access_objects['group_parents']
983 dc88754b Nanakos Chrysostomos
        nobject_permissions = {}
984 e161c24f Sofia Papagiannaki
        cpath = '/'.join((account, container, ''))
985 e161c24f Sofia Papagiannaki
        cpath_idx = len(cpath)
986 dc88754b Nanakos Chrysostomos
        for path in permissions_path:
987 dc88754b Nanakos Chrysostomos
            allowed = 1
988 e161c24f Sofia Papagiannaki
            name = path[cpath_idx:]
989 dc88754b Nanakos Chrysostomos
            if user != account:
990 dc88754b Nanakos Chrysostomos
                try:
991 dc88754b Nanakos Chrysostomos
                    allowed = access_objects[path]
992 dc88754b Nanakos Chrysostomos
                except KeyError:
993 dc88754b Nanakos Chrysostomos
                    raise NotAllowedError
994 dc88754b Nanakos Chrysostomos
            access_dict, allowed = \
995 dc88754b Nanakos Chrysostomos
                self.permissions.access_get_for_bulk(access_objects[path])
996 dc88754b Nanakos Chrysostomos
            nobject_permissions[name] = (self.ALLOWED[allowed], path,
997 dc88754b Nanakos Chrysostomos
                                         access_dict)
998 dc88754b Nanakos Chrysostomos
        self._lookup_objects(permissions_path)
999 dc88754b Nanakos Chrysostomos
        return nobject_permissions
1000 dc88754b Nanakos Chrysostomos
1001 1f96b68d Sofia Papagiannaki
    @debug_method
1002 5d022141 Sofia Papagiannaki
    @backend_method
1003 a9b3f29d Antony Chazapis
    def get_object_permissions(self, user, account, container, name):
1004 067cf1fc Antony Chazapis
        """Return the action allowed on the object, the path
1005 067cf1fc Antony Chazapis
        from which the object gets its permissions from,
1006 a9b3f29d Antony Chazapis
        along with a dictionary containing the permissions."""
1007 2715ade4 Sofia Papagiannaki
1008 067cf1fc Antony Chazapis
        allowed = 'write'
1009 92da0e5a Antony Chazapis
        permissions_path = self._get_permissions_path(account, container, name)
1010 067cf1fc Antony Chazapis
        if user != account:
1011 29148653 Sofia Papagiannaki
            if self.permissions.access_check(permissions_path, self.WRITE,
1012 29148653 Sofia Papagiannaki
                                             user):
1013 067cf1fc Antony Chazapis
                allowed = 'write'
1014 29148653 Sofia Papagiannaki
            elif self.permissions.access_check(permissions_path, self.READ,
1015 29148653 Sofia Papagiannaki
                                               user):
1016 067cf1fc Antony Chazapis
                allowed = 'read'
1017 067cf1fc Antony Chazapis
            else:
1018 067cf1fc Antony Chazapis
                raise NotAllowedError
1019 92da0e5a Antony Chazapis
        self._lookup_object(account, container, name)
1020 29148653 Sofia Papagiannaki
        return (allowed,
1021 29148653 Sofia Papagiannaki
                permissions_path,
1022 29148653 Sofia Papagiannaki
                self.permissions.access_get(permissions_path))
1023 2715ade4 Sofia Papagiannaki
1024 1f96b68d Sofia Papagiannaki
    @debug_method
1025 5d022141 Sofia Papagiannaki
    @backend_method
1026 29148653 Sofia Papagiannaki
    def update_object_permissions(self, user, account, container, name,
1027 29148653 Sofia Papagiannaki
                                  permissions):
1028 a9b3f29d Antony Chazapis
        """Update the permissions associated with the object."""
1029 2715ade4 Sofia Papagiannaki
1030 a9b3f29d Antony Chazapis
        if user != account:
1031 a9b3f29d Antony Chazapis
            raise NotAllowedError
1032 48bb9c89 Sofia Papagiannaki
        path = self._lookup_object(account, container, name,
1033 48bb9c89 Sofia Papagiannaki
                                   lock_container=True)[0]
1034 6f4bce7b Antony Chazapis
        self._check_permissions(path, permissions)
1035 6c9df07c Sofia Papagiannaki
        try:
1036 6c9df07c Sofia Papagiannaki
            self.permissions.access_set(path, permissions)
1037 6c9df07c Sofia Papagiannaki
        except:
1038 6c9df07c Sofia Papagiannaki
            raise ValueError
1039 6c9df07c Sofia Papagiannaki
        else:
1040 6c9df07c Sofia Papagiannaki
            self._report_sharing_change(user, account, path, {'members':
1041 6c9df07c Sofia Papagiannaki
                                        self.permissions.access_members(path)})
1042 2715ade4 Sofia Papagiannaki
1043 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
1044 ebdbac7a Sofia Papagiannaki
        # filtering out only those affected could be more expensive
1045 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
1046 ebdbac7a Sofia Papagiannaki
1047 1f96b68d Sofia Papagiannaki
    @debug_method
1048 5d022141 Sofia Papagiannaki
    @backend_method
1049 a9b3f29d Antony Chazapis
    def get_object_public(self, user, account, container, name):
1050 bb4eafc6 Antony Chazapis
        """Return the public id of the object if applicable."""
1051 2715ade4 Sofia Papagiannaki
1052 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1053 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
1054 bb4eafc6 Antony Chazapis
        p = self.permissions.public_get(path)
1055 bb4eafc6 Antony Chazapis
        return p
1056 2715ade4 Sofia Papagiannaki
1057 1f96b68d Sofia Papagiannaki
    @debug_method
1058 5d022141 Sofia Papagiannaki
    @backend_method
1059 a9b3f29d Antony Chazapis
    def update_object_public(self, user, account, container, name, public):
1060 a9b3f29d Antony Chazapis
        """Update the public status of the object."""
1061 2715ade4 Sofia Papagiannaki
1062 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
1063 48bb9c89 Sofia Papagiannaki
        path = self._lookup_object(account, container, name,
1064 48bb9c89 Sofia Papagiannaki
                                   lock_container=True)[0]
1065 0f9d752c Antony Chazapis
        if not public:
1066 0f9d752c Antony Chazapis
            self.permissions.public_unset(path)
1067 0f9d752c Antony Chazapis
        else:
1068 56f3c759 Sofia Papagiannaki
            self.permissions.public_set(
1069 adce84cd Sofia Papagiannaki
                path, self.public_url_security, self.public_url_alphabet)
1070 2715ade4 Sofia Papagiannaki
1071 1f96b68d Sofia Papagiannaki
    @debug_method
1072 5d022141 Sofia Papagiannaki
    @backend_method
1073 a9b3f29d Antony Chazapis
    def get_object_hashmap(self, user, account, container, name, version=None):
1074 a9b3f29d Antony Chazapis
        """Return the object's size and a list with partial hashes."""
1075 2715ade4 Sofia Papagiannaki
1076 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1077 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1078 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
1079 adce84cd Sofia Papagiannaki
        if props[self.HASH] is None:
1080 adce84cd Sofia Papagiannaki
            return 0, ()
1081 1e47e49d Chrysostomos Nanakos
        if props[self.HASH].startswith('archip:'):
1082 1e47e49d Chrysostomos Nanakos
            hashmap = self.store.map_get_archipelago(props[self.HASH],
1083 1e47e49d Chrysostomos Nanakos
                                                     props[self.SIZE])
1084 1e47e49d Chrysostomos Nanakos
            return props[self.SIZE], [x for x in hashmap]
1085 1e47e49d Chrysostomos Nanakos
        else:
1086 1e47e49d Chrysostomos Nanakos
            hashmap = self.store.map_get(self._unhexlify_hash(
1087 1e47e49d Chrysostomos Nanakos
                props[self.HASH]))
1088 1e47e49d Chrysostomos Nanakos
            return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
1089 2715ade4 Sofia Papagiannaki
1090 29148653 Sofia Papagiannaki
    def _update_object_hash(self, user, account, container, name, size, type,
1091 29148653 Sofia Papagiannaki
                            hash, checksum, domain, meta, replace_meta,
1092 29148653 Sofia Papagiannaki
                            permissions, src_node=None, src_version_id=None,
1093 9e3a38bb Sofia Papagiannaki
                            is_copy=False, report_size_change=True):
1094 b9064632 Antony Chazapis
        if permissions is not None and user != account:
1095 b9064632 Antony Chazapis
            raise NotAllowedError
1096 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
1097 b9064632 Antony Chazapis
        if permissions is not None:
1098 b9064632 Antony Chazapis
            path = '/'.join((account, container, name))
1099 b9064632 Antony Chazapis
            self._check_permissions(path, permissions)
1100 2715ade4 Sofia Papagiannaki
1101 b9064632 Antony Chazapis
        account_path, account_node = self._lookup_account(account, True)
1102 2715ade4 Sofia Papagiannaki
        container_path, container_node = self._lookup_container(
1103 2715ade4 Sofia Papagiannaki
            account, container)
1104 fed9c5c7 Sofia Papagiannaki
1105 2715ade4 Sofia Papagiannaki
        path, node = self._put_object_node(
1106 2715ade4 Sofia Papagiannaki
            container_path, container_node, name)
1107 0f510652 Sofia Papagiannaki
        pre_version_id, dest_version_id = self._put_version_duplicate(
1108 0f510652 Sofia Papagiannaki
            user, node, src_node=src_node, size=size, type=type, hash=hash,
1109 0f510652 Sofia Papagiannaki
            checksum=checksum, is_copy=is_copy,
1110 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=1)
1111 2715ade4 Sofia Papagiannaki
1112 f9ea264b Antony Chazapis
        # Handle meta.
1113 f9ea264b Antony Chazapis
        if src_version_id is None:
1114 f9ea264b Antony Chazapis
            src_version_id = pre_version_id
1115 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1116 ad9ada51 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace_meta)
1117 2715ade4 Sofia Papagiannaki
1118 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, pre_version_id,
1119 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
1120 813e42e5 Antony Chazapis
        size_delta = size - del_size
1121 fed9c5c7 Sofia Papagiannaki
        if size_delta > 0:
1122 fed9c5c7 Sofia Papagiannaki
            # Check account quota.
1123 fed9c5c7 Sofia Papagiannaki
            if not self.using_external_quotaholder:
1124 29148653 Sofia Papagiannaki
                account_quota = long(self._get_policy(
1125 29148653 Sofia Papagiannaki
                    account_node, is_account_policy=True)['quota'])
1126 29148653 Sofia Papagiannaki
                account_usage = self._get_statistics(account_node,
1127 29148653 Sofia Papagiannaki
                                                     compute=True)[1]
1128 d3655326 Sofia Papagiannaki
                if (account_quota > 0 and account_usage > account_quota):
1129 fed9c5c7 Sofia Papagiannaki
                    raise QuotaError(
1130 fed9c5c7 Sofia Papagiannaki
                        'Account quota exceeded: limit: %s, usage: %s' % (
1131 29148653 Sofia Papagiannaki
                            account_quota, account_usage))
1132 fed9c5c7 Sofia Papagiannaki
1133 fed9c5c7 Sofia Papagiannaki
            # Check container quota.
1134 29148653 Sofia Papagiannaki
            container_quota = long(self._get_policy(
1135 29148653 Sofia Papagiannaki
                container_node, is_account_policy=False)['quota'])
1136 fed9c5c7 Sofia Papagiannaki
            container_usage = self._get_statistics(container_node)[1]
1137 fed9c5c7 Sofia Papagiannaki
            if (container_quota > 0 and container_usage > container_quota):
1138 fed9c5c7 Sofia Papagiannaki
                # This must be executed in a transaction, so the version is
1139 fed9c5c7 Sofia Papagiannaki
                # never created if it fails.
1140 fed9c5c7 Sofia Papagiannaki
                raise QuotaError(
1141 fed9c5c7 Sofia Papagiannaki
                    'Container quota exceeded: limit: %s, usage: %s' % (
1142 fed9c5c7 Sofia Papagiannaki
                        container_quota, container_usage
1143 fed9c5c7 Sofia Papagiannaki
                    )
1144 fed9c5c7 Sofia Papagiannaki
                )
1145 e20a751d Sofia Papagiannaki
1146 9e3a38bb Sofia Papagiannaki
        if report_size_change:
1147 9e3a38bb Sofia Papagiannaki
            self._report_size_change(
1148 9e3a38bb Sofia Papagiannaki
                user, account, size_delta,
1149 9e3a38bb Sofia Papagiannaki
                {'action': 'object update', 'path': path,
1150 9e3a38bb Sofia Papagiannaki
                 'versions': ','.join([str(dest_version_id)])})
1151 b9064632 Antony Chazapis
        if permissions is not None:
1152 b9064632 Antony Chazapis
            self.permissions.access_set(path, permissions)
1153 29148653 Sofia Papagiannaki
            self._report_sharing_change(
1154 29148653 Sofia Papagiannaki
                user, account, path,
1155 29148653 Sofia Papagiannaki
                {'members': self.permissions.access_members(path)})
1156 2715ade4 Sofia Papagiannaki
1157 29148653 Sofia Papagiannaki
        self._report_object_change(
1158 29148653 Sofia Papagiannaki
            user, account, path,
1159 29148653 Sofia Papagiannaki
            details={'version': dest_version_id, 'action': 'object update'})
1160 f9ea264b Antony Chazapis
        return dest_version_id
1161 2715ade4 Sofia Papagiannaki
1162 1f96b68d Sofia Papagiannaki
    @debug_method
1163 29148653 Sofia Papagiannaki
    def update_object_hashmap(self, user, account, container, name, size, type,
1164 29148653 Sofia Papagiannaki
                              hashmap, checksum, domain, meta=None,
1165 29148653 Sofia Papagiannaki
                              replace_meta=False, permissions=None):
1166 29148653 Sofia Papagiannaki
        """Create/update an object's hashmap and return the new version."""
1167 2715ade4 Sofia Papagiannaki
1168 1e47e49d Chrysostomos Nanakos
        for h in hashmap:
1169 1e47e49d Chrysostomos Nanakos
            if h.startswith('archip_'):
1170 1e47e49d Chrysostomos Nanakos
                raise IllegalOperationError(
1171 1e47e49d Chrysostomos Nanakos
                    'Cannot update Archipelago Volume hashmap.')
1172 78348987 Sofia Papagiannaki
        meta = meta or {}
1173 2715ade4 Sofia Papagiannaki
        if size == 0:  # No such thing as an empty hashmap.
1174 6d64339e Antony Chazapis
            hashmap = [self.put_block('')]
1175 1c2fc0ff Antony Chazapis
        map = HashMap(self.block_size, self.hash_algorithm)
1176 3a5994a8 Sofia Papagiannaki
        map.extend([self._unhexlify_hash(x) for x in hashmap])
1177 7ca7bb08 Antony Chazapis
        missing = self.store.block_search(map)
1178 a9b3f29d Antony Chazapis
        if missing:
1179 a9b3f29d Antony Chazapis
            ie = IndexError()
1180 dd71f493 Antony Chazapis
            ie.data = [binascii.hexlify(x) for x in missing]
1181 a9b3f29d Antony Chazapis
            raise ie
1182 2715ade4 Sofia Papagiannaki
1183 1c2fc0ff Antony Chazapis
        hash = map.hash()
1184 133e3fcf Sofia Papagiannaki
        hexlified = binascii.hexlify(hash)
1185 683d4324 Sofia Papagiannaki
        # _update_object_hash() locks destination path
1186 29148653 Sofia Papagiannaki
        dest_version_id = self._update_object_hash(
1187 29148653 Sofia Papagiannaki
            user, account, container, name, size, type, hexlified, checksum,
1188 29148653 Sofia Papagiannaki
            domain, meta, replace_meta, permissions)
1189 7ca7bb08 Antony Chazapis
        self.store.map_put(hash, map)
1190 133e3fcf Sofia Papagiannaki
        return dest_version_id, hexlified
1191 2715ade4 Sofia Papagiannaki
1192 1f96b68d Sofia Papagiannaki
    @debug_method
1193 5d022141 Sofia Papagiannaki
    @backend_method
1194 29148653 Sofia Papagiannaki
    def update_object_checksum(self, user, account, container, name, version,
1195 29148653 Sofia Papagiannaki
                               checksum):
1196 33b4e4a6 Antony Chazapis
        """Update an object's checksum."""
1197 2715ade4 Sofia Papagiannaki
1198 29148653 Sofia Papagiannaki
        # Update objects with greater version and same hashmap
1199 29148653 Sofia Papagiannaki
        # and size (fix metadata updates).
1200 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
1201 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1202 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
1203 33b4e4a6 Antony Chazapis
        props = self._get_version(node, version)
1204 33b4e4a6 Antony Chazapis
        versions = self.node.node_get_versions(node)
1205 33b4e4a6 Antony Chazapis
        for x in versions:
1206 29148653 Sofia Papagiannaki
            if (x[self.SERIAL] >= int(version) and
1207 29148653 Sofia Papagiannaki
                x[self.HASH] == props[self.HASH] and
1208 29148653 Sofia Papagiannaki
                    x[self.SIZE] == props[self.SIZE]):
1209 2715ade4 Sofia Papagiannaki
                self.node.version_put_property(
1210 2715ade4 Sofia Papagiannaki
                    x[self.SERIAL], 'checksum', checksum)
1211 2715ade4 Sofia Papagiannaki
1212 29148653 Sofia Papagiannaki
    def _copy_object(self, user, src_account, src_container, src_name,
1213 29148653 Sofia Papagiannaki
                     dest_account, dest_container, dest_name, type,
1214 29148653 Sofia Papagiannaki
                     dest_domain=None, dest_meta=None, replace_meta=False,
1215 29148653 Sofia Papagiannaki
                     permissions=None, src_version=None, is_move=False,
1216 29148653 Sofia Papagiannaki
                     delimiter=None):
1217 9e3a38bb Sofia Papagiannaki
1218 9e3a38bb Sofia Papagiannaki
        report_size_change = not is_move
1219 78348987 Sofia Papagiannaki
        dest_meta = dest_meta or {}
1220 4d15c94e Sofia Papagiannaki
        dest_version_ids = []
1221 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, src_account, src_container, src_name)
1222 a06c276e Sofia Papagiannaki
1223 a06c276e Sofia Papagiannaki
        src_container_path = '/'.join((src_account, src_container))
1224 a06c276e Sofia Papagiannaki
        dest_container_path = '/'.join((dest_account, dest_container))
1225 a06c276e Sofia Papagiannaki
        # Lock container paths in alphabetical order
1226 a06c276e Sofia Papagiannaki
        if src_container_path < dest_container_path:
1227 a06c276e Sofia Papagiannaki
            self._lookup_container(src_account, src_container)
1228 a06c276e Sofia Papagiannaki
            self._lookup_container(dest_account, dest_container)
1229 a06c276e Sofia Papagiannaki
        else:
1230 a06c276e Sofia Papagiannaki
            self._lookup_container(dest_account, dest_container)
1231 a06c276e Sofia Papagiannaki
            self._lookup_container(src_account, src_container)
1232 a06c276e Sofia Papagiannaki
1233 a06c276e Sofia Papagiannaki
        path, node = self._lookup_object(src_account, src_container, src_name)
1234 1730b3bf chazapis
        # TODO: Will do another fetch of the properties in duplicate version...
1235 2715ade4 Sofia Papagiannaki
        props = self._get_version(
1236 2715ade4 Sofia Papagiannaki
            node, src_version)  # Check to see if source exists.
1237 b9064632 Antony Chazapis
        src_version_id = props[self.SERIAL]
1238 b9064632 Antony Chazapis
        hash = props[self.HASH]
1239 b9064632 Antony Chazapis
        size = props[self.SIZE]
1240 2715ade4 Sofia Papagiannaki
        is_copy = not is_move and (src_account, src_container, src_name) != (
1241 2715ade4 Sofia Papagiannaki
            dest_account, dest_container, dest_name)  # New uuid.
1242 29148653 Sofia Papagiannaki
        dest_version_ids.append(self._update_object_hash(
1243 29148653 Sofia Papagiannaki
            user, dest_account, dest_container, dest_name, size, type, hash,
1244 29148653 Sofia Papagiannaki
            None, dest_domain, dest_meta, replace_meta, permissions,
1245 9e3a38bb Sofia Papagiannaki
            src_node=node, src_version_id=src_version_id, is_copy=is_copy,
1246 9e3a38bb Sofia Papagiannaki
            report_size_change=report_size_change))
1247 29148653 Sofia Papagiannaki
        if is_move and ((src_account, src_container, src_name) !=
1248 29148653 Sofia Papagiannaki
                        (dest_account, dest_container, dest_name)):
1249 9e3a38bb Sofia Papagiannaki
            self._delete_object(user, src_account, src_container, src_name,
1250 9e3a38bb Sofia Papagiannaki
                                report_size_change=report_size_change)
1251 2715ade4 Sofia Papagiannaki
1252 4d15c94e Sofia Papagiannaki
        if delimiter:
1253 29148653 Sofia Papagiannaki
            prefix = (src_name + delimiter if not
1254 29148653 Sofia Papagiannaki
                      src_name.endswith(delimiter) else src_name)
1255 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
1256 29148653 Sofia Papagiannaki
                user, src_account, src_container, prefix, delimiter=None,
1257 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
1258 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
1259 2715ade4 Sofia Papagiannaki
            src_names.sort(key=lambda x: x[2])  # order by nodes
1260 4d15c94e Sofia Papagiannaki
            paths = [elem[0] for elem in src_names]
1261 4d15c94e Sofia Papagiannaki
            nodes = [elem[2] for elem in src_names]
1262 29148653 Sofia Papagiannaki
            # TODO: Will do another fetch of the properties
1263 29148653 Sofia Papagiannaki
            # in duplicate version...
1264 2715ade4 Sofia Papagiannaki
            props = self._get_versions(nodes)  # Check to see if source exists.
1265 2715ade4 Sofia Papagiannaki
1266 4d15c94e Sofia Papagiannaki
            for prop, path, node in zip(props, paths, nodes):
1267 4d15c94e Sofia Papagiannaki
                src_version_id = prop[self.SERIAL]
1268 4d15c94e Sofia Papagiannaki
                hash = prop[self.HASH]
1269 4d15c94e Sofia Papagiannaki
                vtype = prop[self.TYPE]
1270 07867f70 Sofia Papagiannaki
                size = prop[self.SIZE]
1271 2715ade4 Sofia Papagiannaki
                dest_prefix = dest_name + delimiter if not dest_name.endswith(
1272 2715ade4 Sofia Papagiannaki
                    delimiter) else dest_name
1273 4d15c94e Sofia Papagiannaki
                vdest_name = path.replace(prefix, dest_prefix, 1)
1274 683d4324 Sofia Papagiannaki
                # _update_object_hash() locks destination path
1275 29148653 Sofia Papagiannaki
                dest_version_ids.append(self._update_object_hash(
1276 29148653 Sofia Papagiannaki
                    user, dest_account, dest_container, vdest_name, size,
1277 29148653 Sofia Papagiannaki
                    vtype, hash, None, dest_domain, meta={},
1278 29148653 Sofia Papagiannaki
                    replace_meta=False, permissions=None, src_node=node,
1279 277faddf Sofia Papagiannaki
                    src_version_id=src_version_id, is_copy=is_copy,
1280 277faddf Sofia Papagiannaki
                    report_size_change=report_size_change))
1281 29148653 Sofia Papagiannaki
                if is_move and ((src_account, src_container, src_name) !=
1282 29148653 Sofia Papagiannaki
                                (dest_account, dest_container, dest_name)):
1283 277faddf Sofia Papagiannaki
                    self._delete_object(user, src_account, src_container, path,
1284 277faddf Sofia Papagiannaki
                                        report_size_change=report_size_change)
1285 29148653 Sofia Papagiannaki
        return (dest_version_ids[0] if len(dest_version_ids) == 1 else
1286 29148653 Sofia Papagiannaki
                dest_version_ids)
1287 2715ade4 Sofia Papagiannaki
1288 1f96b68d Sofia Papagiannaki
    @debug_method
1289 5d022141 Sofia Papagiannaki
    @backend_method
1290 29148653 Sofia Papagiannaki
    def copy_object(self, user, src_account, src_container, src_name,
1291 29148653 Sofia Papagiannaki
                    dest_account, dest_container, dest_name, type, domain,
1292 29148653 Sofia Papagiannaki
                    meta=None, replace_meta=False, permissions=None,
1293 29148653 Sofia Papagiannaki
                    src_version=None, delimiter=None):
1294 dff7b6f1 Sofia Papagiannaki
        """Copy an object's data and metadata."""
1295 2715ade4 Sofia Papagiannaki
1296 78348987 Sofia Papagiannaki
        meta = meta or {}
1297 29148653 Sofia Papagiannaki
        dest_version_id = self._copy_object(
1298 29148653 Sofia Papagiannaki
            user, src_account, src_container, src_name, dest_account,
1299 29148653 Sofia Papagiannaki
            dest_container, dest_name, type, domain, meta, replace_meta,
1300 29148653 Sofia Papagiannaki
            permissions, src_version, False, delimiter)
1301 46286f5f Antony Chazapis
        return dest_version_id
1302 2715ade4 Sofia Papagiannaki
1303 1f96b68d Sofia Papagiannaki
    @debug_method
1304 5d022141 Sofia Papagiannaki
    @backend_method
1305 29148653 Sofia Papagiannaki
    def move_object(self, user, src_account, src_container, src_name,
1306 29148653 Sofia Papagiannaki
                    dest_account, dest_container, dest_name, type, domain,
1307 29148653 Sofia Papagiannaki
                    meta=None, replace_meta=False, permissions=None,
1308 29148653 Sofia Papagiannaki
                    delimiter=None):
1309 a9b3f29d Antony Chazapis
        """Move an object's data and metadata."""
1310 2715ade4 Sofia Papagiannaki
1311 78348987 Sofia Papagiannaki
        meta = meta or {}
1312 79bb41b7 Antony Chazapis
        if user != src_account:
1313 79bb41b7 Antony Chazapis
            raise NotAllowedError
1314 9e3a38bb Sofia Papagiannaki
        dest_version_id = self._move_object(
1315 29148653 Sofia Papagiannaki
            user, src_account, src_container, src_name, dest_account,
1316 29148653 Sofia Papagiannaki
            dest_container, dest_name, type, domain, meta, replace_meta,
1317 9e3a38bb Sofia Papagiannaki
            permissions, None, delimiter=delimiter)
1318 02c4d2ba Antony Chazapis
        return dest_version_id
1319 2715ade4 Sofia Papagiannaki
1320 29148653 Sofia Papagiannaki
    def _delete_object(self, user, account, container, name, until=None,
1321 9e3a38bb Sofia Papagiannaki
                       delimiter=None, report_size_change=True):
1322 a9b3f29d Antony Chazapis
        if user != account:
1323 a9b3f29d Antony Chazapis
            raise NotAllowedError
1324 2715ade4 Sofia Papagiannaki
1325 48bb9c89 Sofia Papagiannaki
        # lookup object and lock container path also
1326 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1327 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
1328 48bb9c89 Sofia Papagiannaki
1329 a9b3f29d Antony Chazapis
        if until is not None:
1330 c915d3bf Antony Chazapis
            if node is None:
1331 c915d3bf Antony Chazapis
                return
1332 813e42e5 Antony Chazapis
            hashes = []
1333 813e42e5 Antony Chazapis
            size = 0
1334 388ea25f Sofia Papagiannaki
            serials = []
1335 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_NORMAL,
1336 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1337 813e42e5 Antony Chazapis
            hashes += h
1338 813e42e5 Antony Chazapis
            size += s
1339 388ea25f Sofia Papagiannaki
            serials += v
1340 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_HISTORY,
1341 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1342 813e42e5 Antony Chazapis
            hashes += h
1343 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
1344 0a92ff85 Sofia Papagiannaki
                size += s
1345 388ea25f Sofia Papagiannaki
            serials += v
1346 04230536 Antony Chazapis
            for h in hashes:
1347 04230536 Antony Chazapis
                self.store.map_delete(h)
1348 0f510652 Sofia Papagiannaki
            self.node.node_purge(node, until, CLUSTER_DELETED,
1349 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth=1)
1350 a9b3f29d Antony Chazapis
            try:
1351 29148653 Sofia Papagiannaki
                self._get_version(node)
1352 a9b3f29d Antony Chazapis
            except NameError:
1353 0f9d752c Antony Chazapis
                self.permissions.access_clear(path)
1354 0a92ff85 Sofia Papagiannaki
            self._report_size_change(
1355 0a92ff85 Sofia Papagiannaki
                user, account, -size, {
1356 0a92ff85 Sofia Papagiannaki
                    'action': 'object purge',
1357 0a92ff85 Sofia Papagiannaki
                    'path': path,
1358 0a92ff85 Sofia Papagiannaki
                    'versions': ','.join(str(i) for i in serials)
1359 0a92ff85 Sofia Papagiannaki
                }
1360 0a92ff85 Sofia Papagiannaki
            )
1361 a9b3f29d Antony Chazapis
            return
1362 2715ade4 Sofia Papagiannaki
1363 33af031c Sofia Papagiannaki
        if not self._exists(node):
1364 33af031c Sofia Papagiannaki
            raise ItemNotExists('Object is deleted.')
1365 ed2064f8 Christos Stavrakakis
1366 0f510652 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1367 0f510652 Sofia Papagiannaki
            user, node, size=0, type='', hash=None, checksum='',
1368 0f510652 Sofia Papagiannaki
            cluster=CLUSTER_DELETED, update_statistics_ancestors_depth=1)
1369 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, src_version_id,
1370 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
1371 9e3a38bb Sofia Papagiannaki
        if report_size_change:
1372 9e3a38bb Sofia Papagiannaki
            self._report_size_change(
1373 9e3a38bb Sofia Papagiannaki
                user, account, -del_size,
1374 9e3a38bb Sofia Papagiannaki
                {'action': 'object delete',
1375 9e3a38bb Sofia Papagiannaki
                 'path': path,
1376 9e3a38bb Sofia Papagiannaki
                 'versions': ','.join([str(dest_version_id)])})
1377 2715ade4 Sofia Papagiannaki
        self._report_object_change(
1378 2715ade4 Sofia Papagiannaki
            user, account, path, details={'action': 'object delete'})
1379 0f9d752c Antony Chazapis
        self.permissions.access_clear(path)
1380 2715ade4 Sofia Papagiannaki
1381 4d15c94e Sofia Papagiannaki
        if delimiter:
1382 4d15c94e Sofia Papagiannaki
            prefix = name + delimiter if not name.endswith(delimiter) else name
1383 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
1384 29148653 Sofia Papagiannaki
                user, account, container, prefix, delimiter=None,
1385 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
1386 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
1387 4d15c94e Sofia Papagiannaki
            paths = []
1388 4d15c94e Sofia Papagiannaki
            for t in src_names:
1389 2715ade4 Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
1390 2715ade4 Sofia Papagiannaki
                node = t[2]
1391 33af031c Sofia Papagiannaki
                if not self._exists(node):
1392 33af031c Sofia Papagiannaki
                    continue
1393 0f510652 Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(
1394 0f510652 Sofia Papagiannaki
                    user, node, size=0, type='', hash=None, checksum='',
1395 0f510652 Sofia Papagiannaki
                    cluster=CLUSTER_DELETED,
1396 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1397 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
1398 0f510652 Sofia Papagiannaki
                    account, container, src_version_id,
1399 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1400 9e3a38bb Sofia Papagiannaki
                if report_size_change:
1401 9e3a38bb Sofia Papagiannaki
                    self._report_size_change(
1402 9e3a38bb Sofia Papagiannaki
                        user, account, -del_size,
1403 9e3a38bb Sofia Papagiannaki
                        {'action': 'object delete',
1404 9e3a38bb Sofia Papagiannaki
                         'path': path,
1405 9e3a38bb Sofia Papagiannaki
                         'versions': ','.join([str(dest_version_id)])})
1406 2715ade4 Sofia Papagiannaki
                self._report_object_change(
1407 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
1408 4d15c94e Sofia Papagiannaki
                paths.append(path)
1409 4d15c94e Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
1410 2715ade4 Sofia Papagiannaki
1411 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
1412 ebdbac7a Sofia Papagiannaki
        # removing the specific path could be more expensive
1413 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
1414 ebdbac7a Sofia Papagiannaki
1415 1f96b68d Sofia Papagiannaki
    @debug_method
1416 5d022141 Sofia Papagiannaki
    @backend_method
1417 29148653 Sofia Papagiannaki
    def delete_object(self, user, account, container, name, until=None,
1418 29148653 Sofia Papagiannaki
                      prefix='', delimiter=None):
1419 dff7b6f1 Sofia Papagiannaki
        """Delete/purge an object."""
1420 2715ade4 Sofia Papagiannaki
1421 4d15c94e Sofia Papagiannaki
        self._delete_object(user, account, container, name, until, delimiter)
1422 2715ade4 Sofia Papagiannaki
1423 1f96b68d Sofia Papagiannaki
    @debug_method
1424 5d022141 Sofia Papagiannaki
    @backend_method
1425 62f915a1 Antony Chazapis
    def list_versions(self, user, account, container, name):
1426 29148653 Sofia Papagiannaki
        """Return a list of all object (version, version_timestamp) tuples."""
1427 2715ade4 Sofia Papagiannaki
1428 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1429 60b8a083 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1430 97d45f69 Antony Chazapis
        versions = self.node.node_get_versions(node)
1431 29148653 Sofia Papagiannaki
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if
1432 29148653 Sofia Papagiannaki
                x[self.CLUSTER] != CLUSTER_DELETED]
1433 2715ade4 Sofia Papagiannaki
1434 1f96b68d Sofia Papagiannaki
    @debug_method
1435 5d022141 Sofia Papagiannaki
    @backend_method
1436 91fc9266 Sofia Papagiannaki
    def get_uuid(self, user, uuid, check_permissions=True):
1437 37bee317 Antony Chazapis
        """Return the (account, container, name) for the UUID given."""
1438 2715ade4 Sofia Papagiannaki
1439 2bbf1544 Georgios D. Tsoukalas
        info = self.node.latest_uuid(uuid, CLUSTER_NORMAL)
1440 37bee317 Antony Chazapis
        if info is None:
1441 37bee317 Antony Chazapis
            raise NameError
1442 37bee317 Antony Chazapis
        path, serial = info
1443 37bee317 Antony Chazapis
        account, container, name = path.split('/', 2)
1444 91fc9266 Sofia Papagiannaki
        if check_permissions:
1445 ebdbac7a Sofia Papagiannaki
            self._can_read_object(user, account, container, name)
1446 37bee317 Antony Chazapis
        return (account, container, name)
1447 2715ade4 Sofia Papagiannaki
1448 1f96b68d Sofia Papagiannaki
    @debug_method
1449 5d022141 Sofia Papagiannaki
    @backend_method
1450 bb4eafc6 Antony Chazapis
    def get_public(self, user, public):
1451 bb4eafc6 Antony Chazapis
        """Return the (account, container, name) for the public id given."""
1452 2715ade4 Sofia Papagiannaki
1453 56f3c759 Sofia Papagiannaki
        path = self.permissions.public_path(public)
1454 37bee317 Antony Chazapis
        if path is None:
1455 37bee317 Antony Chazapis
            raise NameError
1456 bb4eafc6 Antony Chazapis
        account, container, name = path.split('/', 2)
1457 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1458 bb4eafc6 Antony Chazapis
        return (account, container, name)
1459 2715ade4 Sofia Papagiannaki
1460 a9b3f29d Antony Chazapis
    def get_block(self, hash):
1461 a9b3f29d Antony Chazapis
        """Return a block's data."""
1462 2715ade4 Sofia Papagiannaki
1463 b1aca3e6 Sofia Papagiannaki
        logger.debug("get_block: %s", hash)
1464 1e47e49d Chrysostomos Nanakos
        if hash.startswith('archip_'):
1465 1e47e49d Chrysostomos Nanakos
            block = self.store.block_get_archipelago(hash)
1466 1e47e49d Chrysostomos Nanakos
        else:
1467 1e47e49d Chrysostomos Nanakos
            block = self.store.block_get(self._unhexlify_hash(hash))
1468 7ca7bb08 Antony Chazapis
        if not block:
1469 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Block does not exist')
1470 7ca7bb08 Antony Chazapis
        return block
1471 2715ade4 Sofia Papagiannaki
1472 a9b3f29d Antony Chazapis
    def put_block(self, data):
1473 60b8a083 Antony Chazapis
        """Store a block and return the hash."""
1474 2715ade4 Sofia Papagiannaki
1475 a9b3f29d Antony Chazapis
        logger.debug("put_block: %s", len(data))
1476 7ca7bb08 Antony Chazapis
        return binascii.hexlify(self.store.block_put(data))
1477 2715ade4 Sofia Papagiannaki
1478 a9b3f29d Antony Chazapis
    def update_block(self, hash, data, offset=0):
1479 a9b3f29d Antony Chazapis
        """Update a known block and return the hash."""
1480 2715ade4 Sofia Papagiannaki
1481 a9b3f29d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
1482 1e47e49d Chrysostomos Nanakos
        if hash.startswith('archip_'):
1483 1e47e49d Chrysostomos Nanakos
            raise IllegalOperationError(
1484 1e47e49d Chrysostomos Nanakos
                'Cannot update an Archipelago Volume block.')
1485 a9b3f29d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
1486 a9b3f29d Antony Chazapis
            return self.put_block(data)
1487 3a5994a8 Sofia Papagiannaki
        h = self.store.block_update(self._unhexlify_hash(hash), offset, data)
1488 a9b3f29d Antony Chazapis
        return binascii.hexlify(h)
1489 2715ade4 Sofia Papagiannaki
1490 44ad5860 Antony Chazapis
    # Path functions.
1491 2715ade4 Sofia Papagiannaki
1492 37bee317 Antony Chazapis
    def _generate_uuid(self):
1493 37bee317 Antony Chazapis
        return str(uuidlib.uuid4())
1494 2715ade4 Sofia Papagiannaki
1495 b9064632 Antony Chazapis
    def _put_object_node(self, path, parent, name):
1496 c915d3bf Antony Chazapis
        path = '/'.join((path, name))
1497 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1498 c915d3bf Antony Chazapis
        if node is None:
1499 2e8edd42 Sofia Papagiannaki
            node = self.node.node_create(parent, path)
1500 c915d3bf Antony Chazapis
        return path, node
1501 2715ade4 Sofia Papagiannaki
1502 0f510652 Sofia Papagiannaki
    def _put_path(self, user, parent, path,
1503 0f510652 Sofia Papagiannaki
                  update_statistics_ancestors_depth=None):
1504 62f915a1 Antony Chazapis
        node = self.node.node_create(parent, path)
1505 2715ade4 Sofia Papagiannaki
        self.node.version_create(node, None, 0, '', None, user,
1506 0f510652 Sofia Papagiannaki
                                 self._generate_uuid(), '', CLUSTER_NORMAL,
1507 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth)
1508 62f915a1 Antony Chazapis
        return node
1509 2715ade4 Sofia Papagiannaki
1510 44ad5860 Antony Chazapis
    def _lookup_account(self, account, create=True):
1511 af395b9c Sofia Papagiannaki
        node = self.node.node_lookup(account)
1512 44ad5860 Antony Chazapis
        if node is None and create:
1513 2715ade4 Sofia Papagiannaki
            node = self._put_path(
1514 0f510652 Sofia Papagiannaki
                account, self.ROOTNODE, account,
1515 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=-1)  # User is account.
1516 c915d3bf Antony Chazapis
        return account, node
1517 2715ade4 Sofia Papagiannaki
1518 44ad5860 Antony Chazapis
    def _lookup_container(self, account, container):
1519 b90584d0 Sofia Papagiannaki
        for_update = True if self.lock_container_path else False
1520 c915d3bf Antony Chazapis
        path = '/'.join((account, container))
1521 b90584d0 Sofia Papagiannaki
        node = self.node.node_lookup(path, for_update)
1522 44ad5860 Antony Chazapis
        if node is None:
1523 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Container does not exist')
1524 c915d3bf Antony Chazapis
        return path, node
1525 2715ade4 Sofia Papagiannaki
1526 48bb9c89 Sofia Papagiannaki
    def _lookup_object(self, account, container, name, lock_container=False):
1527 48bb9c89 Sofia Papagiannaki
        if lock_container:
1528 48bb9c89 Sofia Papagiannaki
            self._lookup_container(account, container)
1529 48bb9c89 Sofia Papagiannaki
1530 c915d3bf Antony Chazapis
        path = '/'.join((account, container, name))
1531 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1532 44ad5860 Antony Chazapis
        if node is None:
1533 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Object does not exist')
1534 c915d3bf Antony Chazapis
        return path, node
1535 2715ade4 Sofia Papagiannaki
1536 cf4a7a7b Sofia Papagiannaki
    def _lookup_objects(self, paths):
1537 7efc9f86 Sofia Papagiannaki
        nodes = self.node.node_lookup_bulk(paths)
1538 cf4a7a7b Sofia Papagiannaki
        return paths, nodes
1539 2715ade4 Sofia Papagiannaki
1540 44ad5860 Antony Chazapis
    def _get_properties(self, node, until=None):
1541 44ad5860 Antony Chazapis
        """Return properties until the timestamp given."""
1542 2715ade4 Sofia Papagiannaki
1543 44ad5860 Antony Chazapis
        before = until if until is not None else inf
1544 44ad5860 Antony Chazapis
        props = self.node.version_lookup(node, before, CLUSTER_NORMAL)
1545 44ad5860 Antony Chazapis
        if props is None and until is not None:
1546 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, before, CLUSTER_HISTORY)
1547 44ad5860 Antony Chazapis
        if props is None:
1548 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Path does not exist')
1549 44ad5860 Antony Chazapis
        return props
1550 2715ade4 Sofia Papagiannaki
1551 2e8edd42 Sofia Papagiannaki
    def _get_statistics(self, node, until=None, compute=False):
1552 29148653 Sofia Papagiannaki
        """Return (count, sum of size, timestamp) of everything under node."""
1553 2715ade4 Sofia Papagiannaki
1554 2e8edd42 Sofia Papagiannaki
        if until is not None:
1555 62f915a1 Antony Chazapis
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1556 2e8edd42 Sofia Papagiannaki
        elif compute:
1557 29148653 Sofia Papagiannaki
            stats = self.node.statistics_latest(node,
1558 29148653 Sofia Papagiannaki
                                                except_cluster=CLUSTER_DELETED)
1559 2e8edd42 Sofia Papagiannaki
        else:
1560 2e8edd42 Sofia Papagiannaki
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1561 62f915a1 Antony Chazapis
        if stats is None:
1562 62f915a1 Antony Chazapis
            stats = (0, 0, 0)
1563 62f915a1 Antony Chazapis
        return stats
1564 2715ade4 Sofia Papagiannaki
1565 44ad5860 Antony Chazapis
    def _get_version(self, node, version=None):
1566 44ad5860 Antony Chazapis
        if version is None:
1567 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1568 44ad5860 Antony Chazapis
            if props is None:
1569 7efc9f86 Sofia Papagiannaki
                raise ItemNotExists('Object does not exist')
1570 44ad5860 Antony Chazapis
        else:
1571 07afd277 Antony Chazapis
            try:
1572 07afd277 Antony Chazapis
                version = int(version)
1573 07afd277 Antony Chazapis
            except ValueError:
1574 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1575 d2fc71c9 Sofia Papagiannaki
            props = self.node.version_get_properties(version, node=node)
1576 2c5363a0 Antony Chazapis
            if props is None or props[self.CLUSTER] == CLUSTER_DELETED:
1577 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1578 44ad5860 Antony Chazapis
        return props
1579 4d15c94e Sofia Papagiannaki
1580 7efc9f86 Sofia Papagiannaki
    def _get_versions(self, nodes):
1581 7efc9f86 Sofia Papagiannaki
        return self.node.version_lookup_bulk(nodes, inf, CLUSTER_NORMAL)
1582 2715ade4 Sofia Papagiannaki
1583 0f510652 Sofia Papagiannaki
    def _put_version_duplicate(self, user, node, src_node=None, size=None,
1584 0f510652 Sofia Papagiannaki
                               type=None, hash=None, checksum=None,
1585 0f510652 Sofia Papagiannaki
                               cluster=CLUSTER_NORMAL, is_copy=False,
1586 0f510652 Sofia Papagiannaki
                               update_statistics_ancestors_depth=None):
1587 b9064632 Antony Chazapis
        """Create a new version of the node."""
1588 2715ade4 Sofia Papagiannaki
1589 2715ade4 Sofia Papagiannaki
        props = self.node.version_lookup(
1590 2715ade4 Sofia Papagiannaki
            node if src_node is None else src_node, inf, CLUSTER_NORMAL)
1591 b9064632 Antony Chazapis
        if props is not None:
1592 b9064632 Antony Chazapis
            src_version_id = props[self.SERIAL]
1593 b9064632 Antony Chazapis
            src_hash = props[self.HASH]
1594 b9064632 Antony Chazapis
            src_size = props[self.SIZE]
1595 66ce2ca5 Antony Chazapis
            src_type = props[self.TYPE]
1596 33b4e4a6 Antony Chazapis
            src_checksum = props[self.CHECKSUM]
1597 44ad5860 Antony Chazapis
        else:
1598 b9064632 Antony Chazapis
            src_version_id = None
1599 b9064632 Antony Chazapis
            src_hash = None
1600 b9064632 Antony Chazapis
            src_size = 0
1601 66ce2ca5 Antony Chazapis
            src_type = ''
1602 33b4e4a6 Antony Chazapis
            src_checksum = ''
1603 2715ade4 Sofia Papagiannaki
        if size is None:  # Set metadata.
1604 29148653 Sofia Papagiannaki
            hash = src_hash  # This way hash can be set to None
1605 29148653 Sofia Papagiannaki
                             # (account or container).
1606 b9064632 Antony Chazapis
            size = src_size
1607 66ce2ca5 Antony Chazapis
        if type is None:
1608 66ce2ca5 Antony Chazapis
            type = src_type
1609 33b4e4a6 Antony Chazapis
        if checksum is None:
1610 33b4e4a6 Antony Chazapis
            checksum = src_checksum
1611 2715ade4 Sofia Papagiannaki
        uuid = self._generate_uuid(
1612 2715ade4 Sofia Papagiannaki
        ) if (is_copy or src_version_id is None) else props[self.UUID]
1613 2715ade4 Sofia Papagiannaki
1614 1730b3bf chazapis
        if src_node is None:
1615 1730b3bf chazapis
            pre_version_id = src_version_id
1616 1730b3bf chazapis
        else:
1617 1730b3bf chazapis
            pre_version_id = None
1618 1730b3bf chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1619 1730b3bf chazapis
            if props is not None:
1620 1730b3bf chazapis
                pre_version_id = props[self.SERIAL]
1621 1730b3bf chazapis
        if pre_version_id is not None:
1622 0f510652 Sofia Papagiannaki
            self.node.version_recluster(pre_version_id, CLUSTER_HISTORY,
1623 0f510652 Sofia Papagiannaki
                                        update_statistics_ancestors_depth)
1624 2715ade4 Sofia Papagiannaki
1625 0f510652 Sofia Papagiannaki
        dest_version_id, mtime = self.node.version_create(
1626 0f510652 Sofia Papagiannaki
            node, hash, size, type, src_version_id, user, uuid, checksum,
1627 0f510652 Sofia Papagiannaki
            cluster, update_statistics_ancestors_depth)
1628 ad9ada51 Sofia Papagiannaki
1629 ad9ada51 Sofia Papagiannaki
        self.node.attribute_unset_is_latest(node, dest_version_id)
1630 ad9ada51 Sofia Papagiannaki
1631 1730b3bf chazapis
        return pre_version_id, dest_version_id
1632 2715ade4 Sofia Papagiannaki
1633 ad9ada51 Sofia Papagiannaki
    def _put_metadata_duplicate(self, src_version_id, dest_version_id, domain,
1634 ad9ada51 Sofia Papagiannaki
                                node, meta, replace=False):
1635 4819d34f Antony Chazapis
        if src_version_id is not None:
1636 4819d34f Antony Chazapis
            self.node.attribute_copy(src_version_id, dest_version_id)
1637 4819d34f Antony Chazapis
        if not replace:
1638 2715ade4 Sofia Papagiannaki
            self.node.attribute_del(dest_version_id, domain, (
1639 2715ade4 Sofia Papagiannaki
                k for k, v in meta.iteritems() if v == ''))
1640 ad9ada51 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, (
1641 2715ade4 Sofia Papagiannaki
                (k, v) for k, v in meta.iteritems() if v != ''))
1642 4819d34f Antony Chazapis
        else:
1643 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain)
1644 ad9ada51 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, ((
1645 2715ade4 Sofia Papagiannaki
                k, v) for k, v in meta.iteritems()))
1646 2715ade4 Sofia Papagiannaki
1647 0f510652 Sofia Papagiannaki
    def _put_metadata(self, user, node, domain, meta, replace=False,
1648 0f510652 Sofia Papagiannaki
                      update_statistics_ancestors_depth=None):
1649 44ad5860 Antony Chazapis
        """Create a new version and store metadata."""
1650 2715ade4 Sofia Papagiannaki
1651 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1652 0f510652 Sofia Papagiannaki
            user, node,
1653 29148653 Sofia Papagiannaki
            update_statistics_ancestors_depth=
1654 29148653 Sofia Papagiannaki
            update_statistics_ancestors_depth)
1655 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1656 ad9ada51 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace)
1657 5cc484e1 Antony Chazapis
        return src_version_id, dest_version_id
1658 2715ade4 Sofia Papagiannaki
1659 60b8a083 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
1660 60b8a083 Antony Chazapis
        start = 0
1661 60b8a083 Antony Chazapis
        if marker:
1662 60b8a083 Antony Chazapis
            try:
1663 60b8a083 Antony Chazapis
                start = listing.index(marker) + 1
1664 60b8a083 Antony Chazapis
            except ValueError:
1665 60b8a083 Antony Chazapis
                pass
1666 60b8a083 Antony Chazapis
        if not limit or limit > 10000:
1667 60b8a083 Antony Chazapis
            limit = 10000
1668 60b8a083 Antony Chazapis
        return start, limit
1669 2715ade4 Sofia Papagiannaki
1670 29148653 Sofia Papagiannaki
    def _list_object_properties(self, parent, path, prefix='', delimiter=None,
1671 29148653 Sofia Papagiannaki
                                marker=None, limit=10000, virtual=True,
1672 29148653 Sofia Papagiannaki
                                domain=None, keys=None, until=None,
1673 29148653 Sofia Papagiannaki
                                size_range=None, allowed=None,
1674 29148653 Sofia Papagiannaki
                                all_props=False):
1675 78348987 Sofia Papagiannaki
        keys = keys or []
1676 78348987 Sofia Papagiannaki
        allowed = allowed or []
1677 60b8a083 Antony Chazapis
        cont_prefix = path + '/'
1678 60b8a083 Antony Chazapis
        prefix = cont_prefix + prefix
1679 60b8a083 Antony Chazapis
        start = cont_prefix + marker if marker else None
1680 60b8a083 Antony Chazapis
        before = until if until is not None else inf
1681 4819d34f Antony Chazapis
        filterq = keys if domain else []
1682 7ff57991 Antony Chazapis
        sizeq = size_range
1683 2715ade4 Sofia Papagiannaki
1684 29148653 Sofia Papagiannaki
        objects, prefixes = self.node.latest_version_list(
1685 29148653 Sofia Papagiannaki
            parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED,
1686 29148653 Sofia Papagiannaki
            allowed, domain, filterq, sizeq, all_props)
1687 60b8a083 Antony Chazapis
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1688 43be9afd Sofia Papagiannaki
        objects.sort(key=lambda x: x[0])
1689 371d907a Antony Chazapis
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
1690 cf4a7a7b Sofia Papagiannaki
        return objects
1691 2715ade4 Sofia Papagiannaki
1692 813e42e5 Antony Chazapis
    # Reporting functions.
1693 2715ade4 Sofia Papagiannaki
1694 1f96b68d Sofia Papagiannaki
    @debug_method
1695 5d022141 Sofia Papagiannaki
    @backend_method
1696 78348987 Sofia Papagiannaki
    def _report_size_change(self, user, account, size, details=None):
1697 78348987 Sofia Papagiannaki
        details = details or {}
1698 78348987 Sofia Papagiannaki
1699 8ed3f04c Sofia Papagiannaki
        if size == 0:
1700 8ed3f04c Sofia Papagiannaki
            return
1701 8ed3f04c Sofia Papagiannaki
1702 813e42e5 Antony Chazapis
        account_node = self._lookup_account(account, True)[1]
1703 2e8edd42 Sofia Papagiannaki
        total = self._get_statistics(account_node, compute=True)[1]
1704 813e42e5 Antony Chazapis
        details.update({'user': user, 'total': total})
1705 29148653 Sofia Papagiannaki
        self.messages.append(
1706 29148653 Sofia Papagiannaki
            (QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',),
1707 29148653 Sofia Papagiannaki
             account, QUEUE_INSTANCE_ID, 'diskspace', float(size), details))
1708 7ed99da8 root
1709 c846fad1 Sofia Papagiannaki
        if not self.using_external_quotaholder:
1710 73fbe301 Sofia Papagiannaki
            return
1711 7f1f0464 Georgios D. Tsoukalas
1712 ccfd4e44 Sofia Papagiannaki
        try:
1713 b17e5550 Giorgos Korfiatis
            name = details['path'] if 'path' in details else ''
1714 16f2673e Sofia Papagiannaki
            serial = self.astakosclient.issue_one_commission(
1715 b17e5550 Giorgos Korfiatis
                holder=account,
1716 b17e5550 Giorgos Korfiatis
                source=DEFAULT_SOURCE,
1717 b17e5550 Giorgos Korfiatis
                provisions={'pithos.diskspace': size},
1718 29148653 Sofia Papagiannaki
                name=name)
1719 ccfd4e44 Sofia Papagiannaki
        except BaseException, e:
1720 ccfd4e44 Sofia Papagiannaki
            raise QuotaError(e)
1721 ccfd4e44 Sofia Papagiannaki
        else:
1722 ccfd4e44 Sofia Papagiannaki
            self.serials.append(serial)
1723 0307b47f Georgios D. Tsoukalas
1724 1f96b68d Sofia Papagiannaki
    @debug_method
1725 5d022141 Sofia Papagiannaki
    @backend_method
1726 78348987 Sofia Papagiannaki
    def _report_object_change(self, user, account, path, details=None):
1727 78348987 Sofia Papagiannaki
        details = details or {}
1728 b82d3277 Sofia Papagiannaki
        details.update({'user': user})
1729 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',),
1730 29148653 Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'object', path,
1731 29148653 Sofia Papagiannaki
                              details))
1732 2715ade4 Sofia Papagiannaki
1733 1f96b68d Sofia Papagiannaki
    @debug_method
1734 5d022141 Sofia Papagiannaki
    @backend_method
1735 78348987 Sofia Papagiannaki
    def _report_sharing_change(self, user, account, path, details=None):
1736 78348987 Sofia Papagiannaki
        details = details or {}
1737 a74ba506 Sofia Papagiannaki
        details.update({'user': user})
1738 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',),
1739 29148653 Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'sharing', path,
1740 29148653 Sofia Papagiannaki
                              details))
1741 2715ade4 Sofia Papagiannaki
1742 60b8a083 Antony Chazapis
    # Policy functions.
1743 2715ade4 Sofia Papagiannaki
1744 19ddd41b Sofia Papagiannaki
    def _check_policy(self, policy, is_account_policy=True):
1745 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1746 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1747 60b8a083 Antony Chazapis
        for k in policy.keys():
1748 60b8a083 Antony Chazapis
            if policy[k] == '':
1749 19ddd41b Sofia Papagiannaki
                policy[k] = default_policy.get(k)
1750 60b8a083 Antony Chazapis
        for k, v in policy.iteritems():
1751 60b8a083 Antony Chazapis
            if k == 'quota':
1752 2715ade4 Sofia Papagiannaki
                q = int(v)  # May raise ValueError.
1753 60b8a083 Antony Chazapis
                if q < 0:
1754 60b8a083 Antony Chazapis
                    raise ValueError
1755 60b8a083 Antony Chazapis
            elif k == 'versioning':
1756 5cc484e1 Antony Chazapis
                if v not in ['auto', 'none']:
1757 60b8a083 Antony Chazapis
                    raise ValueError
1758 60b8a083 Antony Chazapis
            else:
1759 60b8a083 Antony Chazapis
                raise ValueError
1760 2715ade4 Sofia Papagiannaki
1761 19ddd41b Sofia Papagiannaki
    def _put_policy(self, node, policy, replace, is_account_policy=True):
1762 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1763 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1764 b2832c6a Antony Chazapis
        if replace:
1765 19ddd41b Sofia Papagiannaki
            for k, v in default_policy.iteritems():
1766 b2832c6a Antony Chazapis
                if k not in policy:
1767 b2832c6a Antony Chazapis
                    policy[k] = v
1768 b2832c6a Antony Chazapis
        self.node.policy_set(node, policy)
1769 2715ade4 Sofia Papagiannaki
1770 19ddd41b Sofia Papagiannaki
    def _get_policy(self, node, is_account_policy=True):
1771 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1772 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1773 19ddd41b Sofia Papagiannaki
        policy = default_policy.copy()
1774 b9064632 Antony Chazapis
        policy.update(self.node.policy_get(node))
1775 b9064632 Antony Chazapis
        return policy
1776 2715ade4 Sofia Papagiannaki
1777 0f510652 Sofia Papagiannaki
    def _apply_versioning(self, account, container, version_id,
1778 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
1779 813e42e5 Antony Chazapis
        """Delete the provided version if such is the policy.
1780 813e42e5 Antony Chazapis
           Return size of object removed.
1781 813e42e5 Antony Chazapis
        """
1782 2715ade4 Sofia Papagiannaki
1783 5cc484e1 Antony Chazapis
        if version_id is None:
1784 813e42e5 Antony Chazapis
            return 0
1785 5cc484e1 Antony Chazapis
        path, node = self._lookup_container(account, container)
1786 19ddd41b Sofia Papagiannaki
        versioning = self._get_policy(
1787 19ddd41b Sofia Papagiannaki
            node, is_account_policy=False)['versioning']
1788 1dd34bdd Sofia Papagiannaki
        if versioning != 'auto':
1789 0f510652 Sofia Papagiannaki
            hash, size = self.node.version_remove(
1790 0f510652 Sofia Papagiannaki
                version_id, update_statistics_ancestors_depth)
1791 5161c672 Antony Chazapis
            self.store.map_delete(hash)
1792 813e42e5 Antony Chazapis
            return size
1793 1dd34bdd Sofia Papagiannaki
        elif self.free_versioning:
1794 fff37615 Sofia Papagiannaki
            return self.node.version_get_properties(
1795 fff37615 Sofia Papagiannaki
                version_id, keys=('size',))[0]
1796 813e42e5 Antony Chazapis
        return 0
1797 2715ade4 Sofia Papagiannaki
1798 a9b3f29d Antony Chazapis
    # Access control functions.
1799 2715ade4 Sofia Papagiannaki
1800 a9b3f29d Antony Chazapis
    def _check_groups(self, groups):
1801 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in groups')
1802 a9b3f29d Antony Chazapis
        pass
1803 2715ade4 Sofia Papagiannaki
1804 a9b3f29d Antony Chazapis
    def _check_permissions(self, path, permissions):
1805 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in permissions')
1806 5e068361 Antony Chazapis
        pass
1807 2715ade4 Sofia Papagiannaki
1808 dc88754b Nanakos Chrysostomos
    def _get_formatted_paths(self, paths):
1809 dc88754b Nanakos Chrysostomos
        formatted = []
1810 ace7592b Sofia Papagiannaki
        if len(paths) == 0:
1811 dc88754b Nanakos Chrysostomos
            return formatted
1812 dc88754b Nanakos Chrysostomos
        props = self.node.get_props(paths)
1813 dc88754b Nanakos Chrysostomos
        if props:
1814 dc88754b Nanakos Chrysostomos
            for prop in props:
1815 dc88754b Nanakos Chrysostomos
                if prop[1].split(';', 1)[0].strip() in (
1816 dc88754b Nanakos Chrysostomos
                        'application/directory', 'application/folder'):
1817 ace7592b Sofia Papagiannaki
                    formatted.append((prop[0].rstrip('/') + '/',
1818 ace7592b Sofia Papagiannaki
                                      self.MATCH_PREFIX))
1819 dc88754b Nanakos Chrysostomos
                formatted.append((prop[0], self.MATCH_EXACT))
1820 dc88754b Nanakos Chrysostomos
        return formatted
1821 dc88754b Nanakos Chrysostomos
1822 16b0ed4a Nanakos Chrysostomos
    def _get_permissions_path(self, account, container, name):
1823 16b0ed4a Nanakos Chrysostomos
        path = '/'.join((account, container, name))
1824 16b0ed4a Nanakos Chrysostomos
        permission_paths = self.permissions.access_inherit(path)
1825 16b0ed4a Nanakos Chrysostomos
        permission_paths.sort()
1826 16b0ed4a Nanakos Chrysostomos
        permission_paths.reverse()
1827 16b0ed4a Nanakos Chrysostomos
        for p in permission_paths:
1828 16b0ed4a Nanakos Chrysostomos
            if p == path:
1829 16b0ed4a Nanakos Chrysostomos
                return p
1830 16b0ed4a Nanakos Chrysostomos
            else:
1831 16b0ed4a Nanakos Chrysostomos
                if p.count('/') < 2:
1832 16b0ed4a Nanakos Chrysostomos
                    continue
1833 16b0ed4a Nanakos Chrysostomos
                node = self.node.node_lookup(p)
1834 16b0ed4a Nanakos Chrysostomos
                props = None
1835 16b0ed4a Nanakos Chrysostomos
                if node is not None:
1836 16b0ed4a Nanakos Chrysostomos
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1837 16b0ed4a Nanakos Chrysostomos
                if props is not None:
1838 ace7592b Sofia Papagiannaki
                    if props[self.TYPE].split(';', 1)[0].strip() in (
1839 ace7592b Sofia Papagiannaki
                            'application/directory', 'application/folder'):
1840 16b0ed4a Nanakos Chrysostomos
                        return p
1841 16b0ed4a Nanakos Chrysostomos
        return None
1842 16b0ed4a Nanakos Chrysostomos
1843 dc88754b Nanakos Chrysostomos
    def _get_permissions_path_bulk(self, account, container, names):
1844 dc88754b Nanakos Chrysostomos
        formatted_paths = []
1845 dc88754b Nanakos Chrysostomos
        for name in names:
1846 dc88754b Nanakos Chrysostomos
            path = '/'.join((account, container, name))
1847 dc88754b Nanakos Chrysostomos
            formatted_paths.append(path)
1848 ace7592b Sofia Papagiannaki
        permission_paths = self.permissions.access_inherit_bulk(
1849 ace7592b Sofia Papagiannaki
            formatted_paths)
1850 5e068361 Antony Chazapis
        permission_paths.sort()
1851 5e068361 Antony Chazapis
        permission_paths.reverse()
1852 dc88754b Nanakos Chrysostomos
        permission_paths_list = []
1853 dc88754b Nanakos Chrysostomos
        lookup_list = []
1854 5e068361 Antony Chazapis
        for p in permission_paths:
1855 dc88754b Nanakos Chrysostomos
            if p in formatted_paths:
1856 dc88754b Nanakos Chrysostomos
                permission_paths_list.append(p)
1857 5e068361 Antony Chazapis
            else:
1858 71dbc012 Antony Chazapis
                if p.count('/') < 2:
1859 71dbc012 Antony Chazapis
                    continue
1860 dc88754b Nanakos Chrysostomos
                lookup_list.append(p)
1861 dc88754b Nanakos Chrysostomos
1862 dc88754b Nanakos Chrysostomos
        if len(lookup_list) > 0:
1863 62e6d12e Nanakos Chrysostomos
            props = self.node.get_props(lookup_list)
1864 dc88754b Nanakos Chrysostomos
            if props:
1865 dc88754b Nanakos Chrysostomos
                for prop in props:
1866 dc88754b Nanakos Chrysostomos
                    if prop[1].split(';', 1)[0].strip() in (
1867 29148653 Sofia Papagiannaki
                            'application/directory', 'application/folder'):
1868 e161c24f Sofia Papagiannaki
                        permission_paths_list.append(prop[0])
1869 dc88754b Nanakos Chrysostomos
1870 dc88754b Nanakos Chrysostomos
        if len(permission_paths_list) > 0:
1871 dc88754b Nanakos Chrysostomos
            return permission_paths_list
1872 dc88754b Nanakos Chrysostomos
1873 5e068361 Antony Chazapis
        return None
1874 2715ade4 Sofia Papagiannaki
1875 ebdbac7a Sofia Papagiannaki
    def _reset_allowed_paths(self):
1876 ebdbac7a Sofia Papagiannaki
        self.read_allowed_paths = defaultdict(set)
1877 ebdbac7a Sofia Papagiannaki
        self.write_allowed_paths = defaultdict(set)
1878 ebdbac7a Sofia Papagiannaki
1879 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=0)
1880 ebdbac7a Sofia Papagiannaki
    def _can_read_account(self, user, account):
1881 ebdbac7a Sofia Papagiannaki
        if user != account:
1882 ebdbac7a Sofia Papagiannaki
            if account not in self._allowed_accounts(user):
1883 ebdbac7a Sofia Papagiannaki
                raise NotAllowedError
1884 ebdbac7a Sofia Papagiannaki
1885 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=1)
1886 ebdbac7a Sofia Papagiannaki
    def _can_write_account(self, user, account):
1887 ebdbac7a Sofia Papagiannaki
        if user != account:
1888 ebdbac7a Sofia Papagiannaki
            raise NotAllowedError
1889 ebdbac7a Sofia Papagiannaki
1890 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=0)
1891 ebdbac7a Sofia Papagiannaki
    def _can_read_container(self, user, account, container):
1892 ebdbac7a Sofia Papagiannaki
        if user != account:
1893 ebdbac7a Sofia Papagiannaki
            if container not in self._allowed_containers(user, account):
1894 ebdbac7a Sofia Papagiannaki
                raise NotAllowedError
1895 ebdbac7a Sofia Papagiannaki
1896 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=1)
1897 ebdbac7a Sofia Papagiannaki
    def _can_write_container(self, user, account, container):
1898 ebdbac7a Sofia Papagiannaki
        if user != account:
1899 ebdbac7a Sofia Papagiannaki
            raise NotAllowedError
1900 ebdbac7a Sofia Papagiannaki
1901 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=0)
1902 ebdbac7a Sofia Papagiannaki
    def _can_read_object(self, user, account, container, name):
1903 a9b3f29d Antony Chazapis
        if user == account:
1904 a9b3f29d Antony Chazapis
            return True
1905 a9b3f29d Antony Chazapis
        path = '/'.join((account, container, name))
1906 aeb2b64f Antony Chazapis
        if self.permissions.public_get(path) is not None:
1907 71dbc012 Antony Chazapis
            return True
1908 5e068361 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1909 71dbc012 Antony Chazapis
        if not path:
1910 71dbc012 Antony Chazapis
            raise NotAllowedError
1911 29148653 Sofia Papagiannaki
        if (not self.permissions.access_check(path, self.READ, user) and not
1912 29148653 Sofia Papagiannaki
                self.permissions.access_check(path, self.WRITE, user)):
1913 a9b3f29d Antony Chazapis
            raise NotAllowedError
1914 2715ade4 Sofia Papagiannaki
1915 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=1)
1916 ebdbac7a Sofia Papagiannaki
    def _can_write_object(self, user, account, container, name):
1917 6f4bce7b Antony Chazapis
        if user == account:
1918 6f4bce7b Antony Chazapis
            return True
1919 6f4bce7b Antony Chazapis
        path = '/'.join((account, container, name))
1920 71dbc012 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1921 71dbc012 Antony Chazapis
        if not path:
1922 71dbc012 Antony Chazapis
            raise NotAllowedError
1923 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.WRITE, user):
1924 a9b3f29d Antony Chazapis
            raise NotAllowedError
1925 2715ade4 Sofia Papagiannaki
1926 a9b3f29d Antony Chazapis
    def _allowed_accounts(self, user):
1927 a9b3f29d Antony Chazapis
        allow = set()
1928 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user):
1929 ebdbac7a Sofia Papagiannaki
            p = path.split('/', 1)[0]
1930 ebdbac7a Sofia Papagiannaki
            allow.add(p)
1931 ebdbac7a Sofia Papagiannaki
        self.read_allowed_paths[user] |= allow
1932 a9b3f29d Antony Chazapis
        return sorted(allow)
1933 2715ade4 Sofia Papagiannaki
1934 a9b3f29d Antony Chazapis
    def _allowed_containers(self, user, account):
1935 a9b3f29d Antony Chazapis
        allow = set()
1936 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user, account):
1937 ebdbac7a Sofia Papagiannaki
            p = path.split('/', 2)[1]
1938 ebdbac7a Sofia Papagiannaki
            allow.add(p)
1939 ebdbac7a Sofia Papagiannaki
        self.read_allowed_paths[user] |= allow
1940 a9b3f29d Antony Chazapis
        return sorted(allow)
1941 5576e6dd Sofia Papagiannaki
1942 5576e6dd Sofia Papagiannaki
    # Domain functions
1943 5576e6dd Sofia Papagiannaki
1944 1f96b68d Sofia Papagiannaki
    @debug_method
1945 5d022141 Sofia Papagiannaki
    @backend_method
1946 e77b7a99 Sofia Papagiannaki
    def get_domain_objects(self, domain, user=None):
1947 a63f36a2 Sofia Papagiannaki
        allowed_paths = self.permissions.access_list_paths(
1948 a63f36a2 Sofia Papagiannaki
            user, include_owned=user is not None, include_containers=False)
1949 7736e11a Sofia Papagiannaki
        if not allowed_paths:
1950 7736e11a Sofia Papagiannaki
            return []
1951 7736e11a Sofia Papagiannaki
        obj_list = self.node.domain_object_list(
1952 7736e11a Sofia Papagiannaki
            domain, allowed_paths, CLUSTER_NORMAL)
1953 5576e6dd Sofia Papagiannaki
        return [(path,
1954 5576e6dd Sofia Papagiannaki
                 self._build_metadata(props, user_defined_meta),
1955 7736e11a Sofia Papagiannaki
                 self.permissions.access_get(path)) for
1956 7736e11a Sofia Papagiannaki
                path, props, user_defined_meta in obj_list]
1957 5576e6dd Sofia Papagiannaki
1958 5576e6dd Sofia Papagiannaki
    # util functions
1959 5576e6dd Sofia Papagiannaki
1960 5576e6dd Sofia Papagiannaki
    def _build_metadata(self, props, user_defined=None,
1961 5576e6dd Sofia Papagiannaki
                        include_user_defined=True):
1962 5576e6dd Sofia Papagiannaki
        meta = {'bytes': props[self.SIZE],
1963 5576e6dd Sofia Papagiannaki
                'type': props[self.TYPE],
1964 5576e6dd Sofia Papagiannaki
                'hash': props[self.HASH],
1965 5576e6dd Sofia Papagiannaki
                'version': props[self.SERIAL],
1966 5576e6dd Sofia Papagiannaki
                'version_timestamp': props[self.MTIME],
1967 5576e6dd Sofia Papagiannaki
                'modified_by': props[self.MUSER],
1968 5576e6dd Sofia Papagiannaki
                'uuid': props[self.UUID],
1969 5576e6dd Sofia Papagiannaki
                'checksum': props[self.CHECKSUM]}
1970 29148653 Sofia Papagiannaki
        if include_user_defined and user_defined is not None:
1971 5576e6dd Sofia Papagiannaki
            meta.update(user_defined)
1972 5576e6dd Sofia Papagiannaki
        return meta
1973 5576e6dd Sofia Papagiannaki
1974 33af031c Sofia Papagiannaki
    def _exists(self, node):
1975 33af031c Sofia Papagiannaki
        try:
1976 33af031c Sofia Papagiannaki
            self._get_version(node)
1977 33af031c Sofia Papagiannaki
        except ItemNotExists:
1978 33af031c Sofia Papagiannaki
            return False
1979 33af031c Sofia Papagiannaki
        else:
1980 33af031c Sofia Papagiannaki
            return True
1981 3a5994a8 Sofia Papagiannaki
1982 3a5994a8 Sofia Papagiannaki
    def _unhexlify_hash(self, hash):
1983 3a5994a8 Sofia Papagiannaki
        try:
1984 3a5994a8 Sofia Papagiannaki
            return binascii.unhexlify(hash)
1985 3a5994a8 Sofia Papagiannaki
        except TypeError:
1986 3a5994a8 Sofia Papagiannaki
            raise InvalidHash(hash)