Statistics
| Branch: | Tag: | Revision:

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

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

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