Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 6e3e5c84

History | View | Annotate | Download (83.7 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
3 a9b3f29d Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 a9b3f29d Antony Chazapis
# without modification, are permitted provided that the following
5 a9b3f29d Antony Chazapis
# conditions are met:
6 2715ade4 Sofia Papagiannaki
#
7 a9b3f29d Antony Chazapis
#   1. Redistributions of source code must retain the above
8 a9b3f29d Antony Chazapis
#      copyright notice, this list of conditions and the following
9 a9b3f29d Antony Chazapis
#      disclaimer.
10 2715ade4 Sofia Papagiannaki
#
11 7ff57991 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 7ff57991 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 a9b3f29d Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 a9b3f29d Antony Chazapis
#      provided with the distribution.
15 2715ade4 Sofia Papagiannaki
#
16 a9b3f29d Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a9b3f29d Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a9b3f29d Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a9b3f29d Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a9b3f29d Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a9b3f29d Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a9b3f29d Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a9b3f29d Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a9b3f29d Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a9b3f29d Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a9b3f29d Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a9b3f29d Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 2715ade4 Sofia Papagiannaki
#
29 a9b3f29d Antony Chazapis
# The views and conclusions contained in the software and
30 a9b3f29d Antony Chazapis
# documentation are those of the authors and should not be
31 a9b3f29d Antony Chazapis
# interpreted as representing official policies, either expressed
32 a9b3f29d Antony Chazapis
# or implied, of GRNET S.A.
33 a9b3f29d Antony Chazapis
34 2c5363a0 Antony Chazapis
import sys
35 37bee317 Antony Chazapis
import uuid as uuidlib
36 a9b3f29d Antony Chazapis
import logging
37 6e147ecc Antony Chazapis
import hashlib
38 a9b3f29d Antony Chazapis
import binascii
39 a9b3f29d Antony Chazapis
40 ebdbac7a Sofia Papagiannaki
from collections import defaultdict
41 9e3a38bb Sofia Papagiannaki
from functools import wraps, partial
42 1f96b68d Sofia Papagiannaki
from traceback import format_exc
43 876d7486 Sofia Papagiannaki
from time import time
44 1f96b68d Sofia Papagiannaki
45 b20f5e4a Chrysostomos Nanakos
from pithos.workers import glue
46 b20f5e4a Chrysostomos Nanakos
from archipelago.common import Segment, Xseg_ctx
47 b20f5e4a Chrysostomos Nanakos
from objpool import ObjectPool
48 b20f5e4a Chrysostomos Nanakos
49 b20f5e4a Chrysostomos Nanakos
50 7ee27246 Georgios D. Tsoukalas
try:
51 7ee27246 Georgios D. Tsoukalas
    from astakosclient import AstakosClient
52 7ee27246 Georgios D. Tsoukalas
except ImportError:
53 7ee27246 Georgios D. Tsoukalas
    AstakosClient = None
54 0307b47f Georgios D. Tsoukalas
55 34f3d4fa Chrysostomos Nanakos
from pithos.backends.base import (
56 34f3d4fa Chrysostomos Nanakos
    DEFAULT_ACCOUNT_QUOTA, DEFAULT_CONTAINER_QUOTA,
57 34f3d4fa Chrysostomos Nanakos
    DEFAULT_CONTAINER_VERSIONING, NotAllowedError, QuotaError,
58 34f3d4fa Chrysostomos Nanakos
    BaseBackend, AccountExists, ContainerExists, AccountNotEmpty,
59 34f3d4fa Chrysostomos Nanakos
    ContainerNotEmpty, ItemNotExists, VersionNotExists,
60 34f3d4fa Chrysostomos Nanakos
    InvalidHash, IllegalOperationError)
61 a9b3f29d Antony Chazapis
62 7ee27246 Georgios D. Tsoukalas
63 7ee27246 Georgios D. Tsoukalas
class DisabledAstakosClient(object):
64 7ee27246 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
65 7ee27246 Georgios D. Tsoukalas
        self.args = args
66 7ee27246 Georgios D. Tsoukalas
        self.kwargs = kwargs
67 7ee27246 Georgios D. Tsoukalas
68 7ee27246 Georgios D. Tsoukalas
    def __getattr__(self, name):
69 7ee27246 Georgios D. Tsoukalas
        m = ("AstakosClient has been disabled, "
70 7ee27246 Georgios D. Tsoukalas
             "yet an attempt to access it was made")
71 7ee27246 Georgios D. Tsoukalas
        raise AssertionError(m)
72 7ee27246 Georgios D. Tsoukalas
73 7ee27246 Georgios D. Tsoukalas
74 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
75 2715ade4 Sofia Papagiannaki
76 6e147ecc Antony Chazapis
class HashMap(list):
77 6e147ecc Antony Chazapis
78 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
79 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
80 6e147ecc Antony Chazapis
        self.blocksize = blocksize
81 6e147ecc Antony Chazapis
        self.blockhash = blockhash
82 6e147ecc Antony Chazapis
83 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
84 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
85 6e147ecc Antony Chazapis
        h.update(v)
86 6e147ecc Antony Chazapis
        return h.digest()
87 6e147ecc Antony Chazapis
88 6e147ecc Antony Chazapis
    def hash(self):
89 6e147ecc Antony Chazapis
        if len(self) == 0:
90 6e147ecc Antony Chazapis
            return self._hash_raw('')
91 6e147ecc Antony Chazapis
        if len(self) == 1:
92 6e147ecc Antony Chazapis
            return self.__getitem__(0)
93 6e147ecc Antony Chazapis
94 6e147ecc Antony Chazapis
        h = list(self)
95 6e147ecc Antony Chazapis
        s = 2
96 6e147ecc Antony Chazapis
        while s < len(h):
97 6e147ecc Antony Chazapis
            s = s * 2
98 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
99 6e147ecc Antony Chazapis
        while len(h) > 1:
100 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
101 6e147ecc Antony Chazapis
        return h[0]
102 5a96180b Antony Chazapis
103 228de81b Antony Chazapis
# Default modules and settings.
104 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
105 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
106 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
107 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
108 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
109 369a7b41 Sofia Papagiannaki
DEFAULT_BLOCK_SIZE = 4 * 1024 * 1024  # 4MB
110 369a7b41 Sofia Papagiannaki
DEFAULT_HASH_ALGORITHM = 'sha256'
111 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
112 29148653 Sofia Papagiannaki
DEFAULT_BLOCK_PARAMS = {'mappool': None, 'blockpool': None}
113 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
114 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
115 4a105ce2 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_ALPHABET = ('0123456789'
116 4a105ce2 Sofia Papagiannaki
                               'abcdefghijklmnopqrstuvwxyz'
117 4a105ce2 Sofia Papagiannaki
                               'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
118 c77af544 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_SECURITY = 16
119 b20f5e4a Chrysostomos Nanakos
DEFAULT_ARCHIPELAGO_CONF_FILE = '/etc/archipelago/archipelago.conf'
120 fa9cae7e Antony Chazapis
121 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
122 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
123 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
124 228de81b Antony Chazapis
125 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
126 44ad5860 Antony Chazapis
127 44ad5860 Antony Chazapis
inf = float('inf')
128 44ad5860 Antony Chazapis
129 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
130 bb4eafc6 Antony Chazapis
131 b17e5550 Giorgos Korfiatis
DEFAULT_SOURCE = 'system'
132 3b8f938b Sofia Papagiannaki
DEFAULT_DISKSPACE_RESOURCE = 'pithos.diskspace'
133 a9b3f29d Antony Chazapis
134 57310278 Sofia Papagiannaki
DEFAULT_MAP_CHECK_INTERVAL = 5  # set to 5 secs
135 876d7486 Sofia Papagiannaki
136 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
137 a9b3f29d Antony Chazapis
138 1c2fc0ff Antony Chazapis
139 5d022141 Sofia Papagiannaki
def backend_method(func):
140 5d022141 Sofia Papagiannaki
    @wraps(func)
141 5d022141 Sofia Papagiannaki
    def wrapper(self, *args, **kw):
142 5d022141 Sofia Papagiannaki
        # if we are inside a database transaction
143 5d022141 Sofia Papagiannaki
        # just proceed with the method execution
144 5d022141 Sofia Papagiannaki
        # otherwise manage a new transaction
145 5d022141 Sofia Papagiannaki
        if self.in_transaction:
146 5d022141 Sofia Papagiannaki
            return func(self, *args, **kw)
147 5d022141 Sofia Papagiannaki
148 5d022141 Sofia Papagiannaki
        try:
149 5d022141 Sofia Papagiannaki
            self.pre_exec()
150 5d022141 Sofia Papagiannaki
            result = func(self, *args, **kw)
151 5d022141 Sofia Papagiannaki
            success_status = True
152 5d022141 Sofia Papagiannaki
            return result
153 5d022141 Sofia Papagiannaki
        except:
154 5d022141 Sofia Papagiannaki
            success_status = False
155 5d022141 Sofia Papagiannaki
            raise
156 5d022141 Sofia Papagiannaki
        finally:
157 5d022141 Sofia Papagiannaki
            self.post_exec(success_status)
158 5d022141 Sofia Papagiannaki
    return wrapper
159 5d022141 Sofia Papagiannaki
160 5d022141 Sofia Papagiannaki
161 1f96b68d Sofia Papagiannaki
def debug_method(func):
162 1f96b68d Sofia Papagiannaki
    @wraps(func)
163 1f96b68d Sofia Papagiannaki
    def wrapper(self, *args, **kw):
164 1f96b68d Sofia Papagiannaki
        try:
165 1f96b68d Sofia Papagiannaki
            result = func(self, *args, **kw)
166 1f96b68d Sofia Papagiannaki
            return result
167 1f96b68d Sofia Papagiannaki
        except:
168 1f96b68d Sofia Papagiannaki
            result = format_exc()
169 1f96b68d Sofia Papagiannaki
            raise
170 1f96b68d Sofia Papagiannaki
        finally:
171 b1aca3e6 Sofia Papagiannaki
            all_args = map(repr, args)
172 1f96b68d Sofia Papagiannaki
            map(all_args.append, ('%s=%s' % (k, v) for k, v in kw.iteritems()))
173 1f96b68d Sofia Papagiannaki
            logger.debug(">>> %s(%s) <<< %s" % (
174 1f96b68d Sofia Papagiannaki
                func.__name__, ', '.join(all_args).rstrip(', '), result))
175 1f96b68d Sofia Papagiannaki
    return wrapper
176 1f96b68d Sofia Papagiannaki
177 1f96b68d Sofia Papagiannaki
178 ebdbac7a Sofia Papagiannaki
def check_allowed_paths(action):
179 ebdbac7a Sofia Papagiannaki
    """Decorator for backend methods checking path access granted to user.
180 ebdbac7a Sofia Papagiannaki

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

185 ebdbac7a Sofia Papagiannaki
    The decorator checks whether the requested path is among the user's allowed
186 ebdbac7a Sofia Papagiannaki
    cached paths.
187 ebdbac7a Sofia Papagiannaki
    If this is the case, the decorator returns immediately to reduce the
188 ebdbac7a Sofia Papagiannaki
    interactions with the database.
189 ebdbac7a Sofia Papagiannaki
    Otherwise, it proceeds with the execution of the decorated method and if
190 ebdbac7a Sofia Papagiannaki
    the method returns successfully (no exceptions are raised), the requested
191 ebdbac7a Sofia Papagiannaki
    path is added to the user's cached allowed paths.
192 ebdbac7a Sofia Papagiannaki

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

228 e9363f82 Antony Chazapis
    Uses modules for SQL functions and storage.
229 a9b3f29d Antony Chazapis
    """
230 2715ade4 Sofia Papagiannaki
231 46286f5f Antony Chazapis
    def __init__(self, db_module=None, db_connection=None,
232 f3b65e8f Antony Chazapis
                 block_module=None, block_path=None, block_umask=None,
233 369a7b41 Sofia Papagiannaki
                 block_size=None, hash_algorithm=None,
234 3173699c Sofia Papagiannaki
                 queue_module=None, queue_hosts=None, queue_exchange=None,
235 f759041f Ilias Tsitsimpis
                 astakos_auth_url=None, service_token=None,
236 16f2673e Sofia Papagiannaki
                 astakosclient_poolsize=None,
237 56f3c759 Sofia Papagiannaki
                 free_versioning=True, block_params=None,
238 4a105ce2 Sofia Papagiannaki
                 public_url_security=None,
239 19ddd41b Sofia Papagiannaki
                 public_url_alphabet=None,
240 19ddd41b Sofia Papagiannaki
                 account_quota_policy=None,
241 19ddd41b Sofia Papagiannaki
                 container_quota_policy=None,
242 b20f5e4a Chrysostomos Nanakos
                 container_versioning_policy=None,
243 b20f5e4a Chrysostomos Nanakos
                 archipelago_conf_file=None,
244 876d7486 Sofia Papagiannaki
                 xseg_pool_size=8,
245 876d7486 Sofia Papagiannaki
                 map_check_interval=None):
246 228de81b Antony Chazapis
        db_module = db_module or DEFAULT_DB_MODULE
247 228de81b Antony Chazapis
        db_connection = db_connection or DEFAULT_DB_CONNECTION
248 228de81b Antony Chazapis
        block_module = block_module or DEFAULT_BLOCK_MODULE
249 228de81b Antony Chazapis
        block_path = block_path or DEFAULT_BLOCK_PATH
250 f3b65e8f Antony Chazapis
        block_umask = block_umask or DEFAULT_BLOCK_UMASK
251 7f1f0464 Georgios D. Tsoukalas
        block_params = block_params or DEFAULT_BLOCK_PARAMS
252 369a7b41 Sofia Papagiannaki
        block_size = block_size or DEFAULT_BLOCK_SIZE
253 369a7b41 Sofia Papagiannaki
        hash_algorithm = hash_algorithm or DEFAULT_HASH_ALGORITHM
254 46286f5f Antony Chazapis
        #queue_module = queue_module or DEFAULT_QUEUE_MODULE
255 19ddd41b Sofia Papagiannaki
        account_quota_policy = account_quota_policy or DEFAULT_ACCOUNT_QUOTA
256 19ddd41b Sofia Papagiannaki
        container_quota_policy = container_quota_policy \
257 19ddd41b Sofia Papagiannaki
            or DEFAULT_CONTAINER_QUOTA
258 19ddd41b Sofia Papagiannaki
        container_versioning_policy = container_versioning_policy \
259 19ddd41b Sofia Papagiannaki
            or DEFAULT_CONTAINER_VERSIONING
260 b20f5e4a Chrysostomos Nanakos
        archipelago_conf_file = archipelago_conf_file \
261 b20f5e4a Chrysostomos Nanakos
            or DEFAULT_ARCHIPELAGO_CONF_FILE
262 876d7486 Sofia Papagiannaki
        map_check_interval = map_check_interval \
263 876d7486 Sofia Papagiannaki
            or DEFAULT_MAP_CHECK_INTERVAL
264 19ddd41b Sofia Papagiannaki
265 19ddd41b Sofia Papagiannaki
        self.default_account_policy = {'quota': account_quota_policy}
266 19ddd41b Sofia Papagiannaki
        self.default_container_policy = {
267 19ddd41b Sofia Papagiannaki
            'quota': container_quota_policy,
268 19ddd41b Sofia Papagiannaki
            'versioning': container_versioning_policy
269 19ddd41b Sofia Papagiannaki
        }
270 f4fbb0fa Sofia Papagiannaki
        #queue_hosts = queue_hosts or DEFAULT_QUEUE_HOSTS
271 f4fbb0fa Sofia Papagiannaki
        #queue_exchange = queue_exchange or DEFAULT_QUEUE_EXCHANGE
272 7f1f0464 Georgios D. Tsoukalas
273 369a7b41 Sofia Papagiannaki
        self.public_url_security = (public_url_security or
274 29148653 Sofia Papagiannaki
                                    DEFAULT_PUBLIC_URL_SECURITY)
275 369a7b41 Sofia Papagiannaki
        self.public_url_alphabet = (public_url_alphabet or
276 29148653 Sofia Papagiannaki
                                    DEFAULT_PUBLIC_URL_ALPHABET)
277 56f3c759 Sofia Papagiannaki
278 369a7b41 Sofia Papagiannaki
        self.hash_algorithm = hash_algorithm
279 369a7b41 Sofia Papagiannaki
        self.block_size = block_size
280 b1dadd0e Sofia Papagiannaki
        self.free_versioning = free_versioning
281 876d7486 Sofia Papagiannaki
        self.map_check_interval = map_check_interval
282 2715ade4 Sofia Papagiannaki
283 46286f5f Antony Chazapis
        def load_module(m):
284 46286f5f Antony Chazapis
            __import__(m)
285 46286f5f Antony Chazapis
            return sys.modules[m]
286 2715ade4 Sofia Papagiannaki
287 46286f5f Antony Chazapis
        self.db_module = load_module(db_module)
288 46286f5f Antony Chazapis
        self.wrapper = self.db_module.DBWrapper(db_connection)
289 e9363f82 Antony Chazapis
        params = {'wrapper': self.wrapper}
290 e9363f82 Antony Chazapis
        self.permissions = self.db_module.Permissions(**params)
291 2c690fe9 Sofia Papagiannaki
        self.config = self.db_module.Config(**params)
292 16f2673e Sofia Papagiannaki
        self.commission_serials = self.db_module.QuotaholderSerial(**params)
293 e9363f82 Antony Chazapis
        for x in ['READ', 'WRITE']:
294 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
295 e9363f82 Antony Chazapis
        self.node = self.db_module.Node(**params)
296 adce84cd Sofia Papagiannaki
        for x in ['ROOTNODE', 'SERIAL', 'NODE', 'HASH', 'SIZE', 'TYPE',
297 adce84cd Sofia Papagiannaki
                  'MTIME', 'MUSER', 'UUID', 'CHECKSUM', 'CLUSTER',
298 876d7486 Sofia Papagiannaki
                  'MATCH_PREFIX', 'MATCH_EXACT',
299 876d7486 Sofia Papagiannaki
                  'AVAILABLE', 'MAP_CHECK_TIMESTAMP']:
300 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
301 2715ade4 Sofia Papagiannaki
302 ace7592b Sofia Papagiannaki
        self.ALLOWED = ['read', 'write']
303 dc88754b Nanakos Chrysostomos
304 b20f5e4a Chrysostomos Nanakos
        glue.WorkerGlue.setupXsegPool(ObjectPool, Segment, Xseg_ctx,
305 b20f5e4a Chrysostomos Nanakos
                                      cfile=archipelago_conf_file,
306 b20f5e4a Chrysostomos Nanakos
                                      pool_size=xseg_pool_size)
307 46286f5f Antony Chazapis
        self.block_module = load_module(block_module)
308 7f1f0464 Georgios D. Tsoukalas
        self.block_params = block_params
309 7ca7bb08 Antony Chazapis
        params = {'path': block_path,
310 7ca7bb08 Antony Chazapis
                  'block_size': self.block_size,
311 f3b65e8f Antony Chazapis
                  'hash_algorithm': self.hash_algorithm,
312 f3b65e8f Antony Chazapis
                  'umask': block_umask}
313 7f1f0464 Georgios D. Tsoukalas
        params.update(self.block_params)
314 7ca7bb08 Antony Chazapis
        self.store = self.block_module.Store(**params)
315 46286f5f Antony Chazapis
316 f4fbb0fa Sofia Papagiannaki
        if queue_module and queue_hosts:
317 46286f5f Antony Chazapis
            self.queue_module = load_module(queue_module)
318 f4fbb0fa Sofia Papagiannaki
            params = {'hosts': queue_hosts,
319 7f1f0464 Georgios D. Tsoukalas
                      'exchange': queue_exchange,
320 fa9cae7e Antony Chazapis
                      'client_id': QUEUE_CLIENT_ID}
321 46286f5f Antony Chazapis
            self.queue = self.queue_module.Queue(**params)
322 46286f5f Antony Chazapis
        else:
323 46286f5f Antony Chazapis
            class NoQueue:
324 1a239dc1 Sofia Papagiannaki
                def send(self, *args):
325 46286f5f Antony Chazapis
                    pass
326 2715ade4 Sofia Papagiannaki
327 b9a8feec root
                def close(self):
328 b9a8feec root
                    pass
329 2715ade4 Sofia Papagiannaki
330 46286f5f Antony Chazapis
            self.queue = NoQueue()
331 2715ade4 Sofia Papagiannaki
332 f759041f Ilias Tsitsimpis
        self.astakos_auth_url = astakos_auth_url
333 16f2673e Sofia Papagiannaki
        self.service_token = service_token
334 7ee27246 Georgios D. Tsoukalas
335 f759041f Ilias Tsitsimpis
        if not astakos_auth_url or not AstakosClient:
336 7ee27246 Georgios D. Tsoukalas
            self.astakosclient = DisabledAstakosClient(
337 f759041f Ilias Tsitsimpis
                service_token, astakos_auth_url,
338 7ee27246 Georgios D. Tsoukalas
                use_pool=True,
339 7ee27246 Georgios D. Tsoukalas
                pool_size=astakosclient_poolsize)
340 7ee27246 Georgios D. Tsoukalas
        else:
341 7ee27246 Georgios D. Tsoukalas
            self.astakosclient = AstakosClient(
342 f759041f Ilias Tsitsimpis
                service_token, astakos_auth_url,
343 7ee27246 Georgios D. Tsoukalas
                use_pool=True,
344 7ee27246 Georgios D. Tsoukalas
                pool_size=astakosclient_poolsize)
345 b336e6fa Georgios D. Tsoukalas
346 7ed99da8 root
        self.serials = []
347 a7f7699d root
        self.messages = []
348 0307b47f Georgios D. Tsoukalas
349 9e3a38bb Sofia Papagiannaki
        self._move_object = partial(self._copy_object, is_move=True)
350 9e3a38bb Sofia Papagiannaki
351 f6d499b0 Sofia Papagiannaki
        self.lock_container_path = False
352 f6d499b0 Sofia Papagiannaki
353 5d022141 Sofia Papagiannaki
        self.in_transaction = False
354 5d022141 Sofia Papagiannaki
355 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
356 ebdbac7a Sofia Papagiannaki
357 d47565d8 Buildbot
    def pre_exec(self, lock_container_path=False):
358 d47565d8 Buildbot
        self.lock_container_path = lock_container_path
359 cc62d2ab Sofia Papagiannaki
        self.wrapper.execute()
360 5d022141 Sofia Papagiannaki
        self.serials = []
361 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
362 5d022141 Sofia Papagiannaki
        self.in_transaction = True
363 cc62d2ab Sofia Papagiannaki
364 d47565d8 Buildbot
    def post_exec(self, success_status=True):
365 cc62d2ab Sofia Papagiannaki
        if success_status:
366 cc62d2ab Sofia Papagiannaki
            # send messages produced
367 cc62d2ab Sofia Papagiannaki
            for m in self.messages:
368 cc62d2ab Sofia Papagiannaki
                self.queue.send(*m)
369 cc62d2ab Sofia Papagiannaki
370 cc62d2ab Sofia Papagiannaki
            # register serials
371 cc62d2ab Sofia Papagiannaki
            if self.serials:
372 cc62d2ab Sofia Papagiannaki
                self.commission_serials.insert_many(
373 cc62d2ab Sofia Papagiannaki
                    self.serials)
374 cc62d2ab Sofia Papagiannaki
375 cc62d2ab Sofia Papagiannaki
                # commit to ensure that the serials are registered
376 cc62d2ab Sofia Papagiannaki
                # even if resolve commission fails
377 cc62d2ab Sofia Papagiannaki
                self.wrapper.commit()
378 cc62d2ab Sofia Papagiannaki
379 cc62d2ab Sofia Papagiannaki
                # start new transaction
380 cc62d2ab Sofia Papagiannaki
                self.wrapper.execute()
381 cc62d2ab Sofia Papagiannaki
382 cc62d2ab Sofia Papagiannaki
                r = self.astakosclient.resolve_commissions(
383 29148653 Sofia Papagiannaki
                    accept_serials=self.serials,
384 29148653 Sofia Papagiannaki
                    reject_serials=[])
385 cc62d2ab Sofia Papagiannaki
                self.commission_serials.delete_many(
386 cc62d2ab Sofia Papagiannaki
                    r['accepted'])
387 cc62d2ab Sofia Papagiannaki
388 cc62d2ab Sofia Papagiannaki
            self.wrapper.commit()
389 cc62d2ab Sofia Papagiannaki
        else:
390 cc62d2ab Sofia Papagiannaki
            if self.serials:
391 135f864e Sofia Papagiannaki
                r = self.astakosclient.resolve_commissions(
392 cc62d2ab Sofia Papagiannaki
                    accept_serials=[],
393 cc62d2ab Sofia Papagiannaki
                    reject_serials=self.serials)
394 96f9f6fd Sofia Papagiannaki
                self.commission_serials.delete_many(
395 96f9f6fd Sofia Papagiannaki
                    r['rejected'])
396 cc62d2ab Sofia Papagiannaki
            self.wrapper.rollback()
397 5d022141 Sofia Papagiannaki
        self.in_transaction = False
398 cc62d2ab Sofia Papagiannaki
399 d14fe290 Antony Chazapis
    def close(self):
400 d14fe290 Antony Chazapis
        self.wrapper.close()
401 b9a8feec root
        self.queue.close()
402 2715ade4 Sofia Papagiannaki
403 c846fad1 Sofia Papagiannaki
    @property
404 c846fad1 Sofia Papagiannaki
    def using_external_quotaholder(self):
405 7ee27246 Georgios D. Tsoukalas
        return not isinstance(self.astakosclient, DisabledAstakosClient)
406 c846fad1 Sofia Papagiannaki
407 1f96b68d Sofia Papagiannaki
    @debug_method
408 5d022141 Sofia Papagiannaki
    @backend_method
409 e5b77cde Sofia Papagiannaki
    @list_method
410 a9b3f29d Antony Chazapis
    def list_accounts(self, user, marker=None, limit=10000):
411 a9b3f29d Antony Chazapis
        """Return a list of accounts the user can access."""
412 2715ade4 Sofia Papagiannaki
413 e5b77cde Sofia Papagiannaki
        return self._allowed_accounts(user)
414 2715ade4 Sofia Papagiannaki
415 3b8f938b Sofia Papagiannaki
    def _get_account_quotas(self, account):
416 3b8f938b Sofia Papagiannaki
        """Get account usage from astakos."""
417 3b8f938b Sofia Papagiannaki
418 3b8f938b Sofia Papagiannaki
        quotas = self.astakosclient.service_get_quotas(account)[account]
419 3b8f938b Sofia Papagiannaki
        return quotas.get(DEFAULT_SOURCE, {}).get(DEFAULT_DISKSPACE_RESOURCE,
420 3b8f938b Sofia Papagiannaki
                                                  {})
421 3b8f938b Sofia Papagiannaki
422 ec6f741b Sofia Papagiannaki
    def _get_account_quotas(self, account):
423 ec6f741b Sofia Papagiannaki
        """Get account usage from astakos."""
424 ec6f741b Sofia Papagiannaki
425 ec6f741b Sofia Papagiannaki
        quotas = self.astakosclient.service_get_quotas(account)[account]
426 ec6f741b Sofia Papagiannaki
        return quotas.get(DEFAULT_SOURCE, {}).get(DEFAULT_DISKSPACE_RESOURCE,
427 ec6f741b Sofia Papagiannaki
                                                  {})
428 ec6f741b Sofia Papagiannaki
429 1f96b68d Sofia Papagiannaki
    @debug_method
430 5d022141 Sofia Papagiannaki
    @backend_method
431 3b8f938b Sofia Papagiannaki
    def get_account_meta(self, user, account, domain, until=None,
432 3b8f938b Sofia Papagiannaki
                         include_user_defined=True):
433 cb69c154 Antony Chazapis
        """Return a dictionary with the account metadata for the domain."""
434 2715ade4 Sofia Papagiannaki
435 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
436 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, user == account)
437 a9b3f29d Antony Chazapis
        if user != account:
438 ebdbac7a Sofia Papagiannaki
            if until or (node is None):
439 a9b3f29d Antony Chazapis
                raise NotAllowedError
440 a9b3f29d Antony Chazapis
        try:
441 44ad5860 Antony Chazapis
            props = self._get_properties(node, until)
442 2c5363a0 Antony Chazapis
            mtime = props[self.MTIME]
443 a9b3f29d Antony Chazapis
        except NameError:
444 62f915a1 Antony Chazapis
            props = None
445 a9b3f29d Antony Chazapis
            mtime = until
446 2e8edd42 Sofia Papagiannaki
        count, bytes, tstamp = self._get_statistics(node, until, compute=True)
447 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
448 a9b3f29d Antony Chazapis
        if until is None:
449 a9b3f29d Antony Chazapis
            modified = tstamp
450 a9b3f29d Antony Chazapis
        else:
451 2715ade4 Sofia Papagiannaki
            modified = self._get_statistics(
452 2e8edd42 Sofia Papagiannaki
                node, compute=True)[2]  # Overall last modification.
453 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
454 2715ade4 Sofia Papagiannaki
455 a9b3f29d Antony Chazapis
        if user != account:
456 a9b3f29d Antony Chazapis
            meta = {'name': account}
457 a9b3f29d Antony Chazapis
        else:
458 44ad5860 Antony Chazapis
            meta = {}
459 82482e2c Antony Chazapis
            if props is not None and include_user_defined:
460 2715ade4 Sofia Papagiannaki
                meta.update(
461 2715ade4 Sofia Papagiannaki
                    dict(self.node.attribute_get(props[self.SERIAL], domain)))
462 a9b3f29d Antony Chazapis
            if until is not None:
463 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
464 44ad5860 Antony Chazapis
            meta.update({'name': account, 'count': count, 'bytes': bytes})
465 0ed09c7c Sofia Papagiannaki
            if self.using_external_quotaholder:
466 3b8f938b Sofia Papagiannaki
                external_quota = self._get_account_quotas(account)
467 02de6286 Sofia Papagiannaki
                meta['bytes'] = external_quota.get('usage', 0)
468 a9b3f29d Antony Chazapis
        meta.update({'modified': modified})
469 a9b3f29d Antony Chazapis
        return meta
470 2715ade4 Sofia Papagiannaki
471 1f96b68d Sofia Papagiannaki
    @debug_method
472 5d022141 Sofia Papagiannaki
    @backend_method
473 cb69c154 Antony Chazapis
    def update_account_meta(self, user, account, domain, meta, replace=False):
474 cb69c154 Antony Chazapis
        """Update the metadata associated with the account for the domain."""
475 2715ade4 Sofia Papagiannaki
476 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
477 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, True)
478 0f510652 Sofia Papagiannaki
        self._put_metadata(user, node, domain, meta, replace,
479 0f510652 Sofia Papagiannaki
                           update_statistics_ancestors_depth=-1)
480 2715ade4 Sofia Papagiannaki
481 1f96b68d Sofia Papagiannaki
    @debug_method
482 5d022141 Sofia Papagiannaki
    @backend_method
483 a9b3f29d Antony Chazapis
    def get_account_groups(self, user, account):
484 29148653 Sofia Papagiannaki
        """Return a dictionary with the user groups defined for the account."""
485 2715ade4 Sofia Papagiannaki
486 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
487 a9b3f29d Antony Chazapis
        if user != account:
488 a9b3f29d Antony Chazapis
            return {}
489 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
490 0f9d752c Antony Chazapis
        return self.permissions.group_dict(account)
491 2715ade4 Sofia Papagiannaki
492 1f96b68d Sofia Papagiannaki
    @debug_method
493 5d022141 Sofia Papagiannaki
    @backend_method
494 a9b3f29d Antony Chazapis
    def update_account_groups(self, user, account, groups, replace=False):
495 a9b3f29d Antony Chazapis
        """Update the groups associated with the account."""
496 2715ade4 Sofia Papagiannaki
497 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
498 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
499 a9b3f29d Antony Chazapis
        self._check_groups(groups)
500 0f9d752c Antony Chazapis
        if replace:
501 0f9d752c Antony Chazapis
            self.permissions.group_destroy(account)
502 0f9d752c Antony Chazapis
        for k, v in groups.iteritems():
503 2715ade4 Sofia Papagiannaki
            if not replace:  # If not already deleted.
504 0f9d752c Antony Chazapis
                self.permissions.group_delete(account, k)
505 0f9d752c Antony Chazapis
            if v:
506 0f9d752c Antony Chazapis
                self.permissions.group_addmany(account, k, v)
507 2715ade4 Sofia Papagiannaki
508 1f96b68d Sofia Papagiannaki
    @debug_method
509 5d022141 Sofia Papagiannaki
    @backend_method
510 3b8f938b Sofia Papagiannaki
    def get_account_policy(self, user, account):
511 b2832c6a Antony Chazapis
        """Return a dictionary with the account policy."""
512 2715ade4 Sofia Papagiannaki
513 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
514 b2832c6a Antony Chazapis
        if user != account:
515 647a5f48 Antony Chazapis
            return {}
516 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
517 19ddd41b Sofia Papagiannaki
        policy = self._get_policy(node, is_account_policy=True)
518 07fcdb96 Sofia Papagiannaki
        if self.using_external_quotaholder:
519 3b8f938b Sofia Papagiannaki
            external_quota = self._get_account_quotas(account)
520 02de6286 Sofia Papagiannaki
            policy['quota'] = external_quota.get('limit', 0)
521 c846fad1 Sofia Papagiannaki
        return policy
522 2715ade4 Sofia Papagiannaki
523 1f96b68d Sofia Papagiannaki
    @debug_method
524 5d022141 Sofia Papagiannaki
    @backend_method
525 b2832c6a Antony Chazapis
    def update_account_policy(self, user, account, policy, replace=False):
526 b2832c6a Antony Chazapis
        """Update the policy associated with the account."""
527 2715ade4 Sofia Papagiannaki
528 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
529 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
530 19ddd41b Sofia Papagiannaki
        self._check_policy(policy, is_account_policy=True)
531 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, replace, is_account_policy=True)
532 2715ade4 Sofia Papagiannaki
533 1f96b68d Sofia Papagiannaki
    @debug_method
534 5d022141 Sofia Papagiannaki
    @backend_method
535 78348987 Sofia Papagiannaki
    def put_account(self, user, account, policy=None):
536 a9b3f29d Antony Chazapis
        """Create a new account with the given name."""
537 2715ade4 Sofia Papagiannaki
538 78348987 Sofia Papagiannaki
        policy = policy or {}
539 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
540 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
541 44ad5860 Antony Chazapis
        if node is not None:
542 7efc9f86 Sofia Papagiannaki
            raise AccountExists('Account already exists')
543 b2832c6a Antony Chazapis
        if policy:
544 19ddd41b Sofia Papagiannaki
            self._check_policy(policy, is_account_policy=True)
545 0f510652 Sofia Papagiannaki
        node = self._put_path(user, self.ROOTNODE, account,
546 0f510652 Sofia Papagiannaki
                              update_statistics_ancestors_depth=-1)
547 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, True, is_account_policy=True)
548 2715ade4 Sofia Papagiannaki
549 1f96b68d Sofia Papagiannaki
    @debug_method
550 5d022141 Sofia Papagiannaki
    @backend_method
551 a9b3f29d Antony Chazapis
    def delete_account(self, user, account):
552 a9b3f29d Antony Chazapis
        """Delete the account with the given name."""
553 2715ade4 Sofia Papagiannaki
554 ebdbac7a Sofia Papagiannaki
        self._can_write_account(user, account)
555 c915d3bf Antony Chazapis
        node = self.node.node_lookup(account)
556 c915d3bf Antony Chazapis
        if node is None:
557 c915d3bf Antony Chazapis
            return
558 0f510652 Sofia Papagiannaki
        if not self.node.node_remove(node,
559 0f510652 Sofia Papagiannaki
                                     update_statistics_ancestors_depth=-1):
560 7efc9f86 Sofia Papagiannaki
            raise AccountNotEmpty('Account is not empty')
561 0f9d752c Antony Chazapis
        self.permissions.group_destroy(account)
562 2715ade4 Sofia Papagiannaki
563 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
564 ebdbac7a Sofia Papagiannaki
        # removing the specific path could be more expensive
565 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
566 ebdbac7a Sofia Papagiannaki
567 1f96b68d Sofia Papagiannaki
    @debug_method
568 5d022141 Sofia Papagiannaki
    @backend_method
569 e5b77cde Sofia Papagiannaki
    @list_method
570 29148653 Sofia Papagiannaki
    def list_containers(self, user, account, marker=None, limit=10000,
571 29148653 Sofia Papagiannaki
                        shared=False, until=None, public=False):
572 62f915a1 Antony Chazapis
        """Return a list of containers existing under an account."""
573 2715ade4 Sofia Papagiannaki
574 ebdbac7a Sofia Papagiannaki
        self._can_read_account(user, account)
575 62f915a1 Antony Chazapis
        if user != account:
576 ebdbac7a Sofia Papagiannaki
            if until:
577 62f915a1 Antony Chazapis
                raise NotAllowedError
578 e5b77cde Sofia Papagiannaki
            return self._allowed_containers(user, account)
579 90ee1eb3 Sofia Papagiannaki
        if shared or public:
580 56ac7c81 Sofia Papagiannaki
            allowed = set()
581 90ee1eb3 Sofia Papagiannaki
            if shared:
582 29148653 Sofia Papagiannaki
                allowed.update([x.split('/', 2)[1] for x in
583 29148653 Sofia Papagiannaki
                               self.permissions.access_list_shared(account)])
584 90ee1eb3 Sofia Papagiannaki
            if public:
585 29148653 Sofia Papagiannaki
                allowed.update([x[0].split('/', 2)[1] for x in
586 29148653 Sofia Papagiannaki
                               self.permissions.public_list(account)])
587 e5b77cde Sofia Papagiannaki
            return sorted(allowed)
588 62f915a1 Antony Chazapis
        node = self.node.node_lookup(account)
589 e5b77cde Sofia Papagiannaki
        return [x[0] for x in self._list_object_properties(
590 2715ade4 Sofia Papagiannaki
            node, account, '', '/', marker, limit, False, None, [], until)]
591 2715ade4 Sofia Papagiannaki
592 1f96b68d Sofia Papagiannaki
    @debug_method
593 5d022141 Sofia Papagiannaki
    @backend_method
594 29148653 Sofia Papagiannaki
    def list_container_meta(self, user, account, container, domain,
595 29148653 Sofia Papagiannaki
                            until=None):
596 29148653 Sofia Papagiannaki
        """Return a list of the container's object meta keys for a domain."""
597 2715ade4 Sofia Papagiannaki
598 ebdbac7a Sofia Papagiannaki
        self._can_read_container(user, account, container)
599 371d907a Antony Chazapis
        allowed = []
600 371d907a Antony Chazapis
        if user != account:
601 371d907a Antony Chazapis
            if until:
602 371d907a Antony Chazapis
                raise NotAllowedError
603 371d907a Antony Chazapis
        path, node = self._lookup_container(account, container)
604 371d907a Antony Chazapis
        before = until if until is not None else inf
605 371d907a Antony Chazapis
        allowed = self._get_formatted_paths(allowed)
606 29148653 Sofia Papagiannaki
        return self.node.latest_attribute_keys(node, domain, before,
607 29148653 Sofia Papagiannaki
                                               CLUSTER_DELETED, allowed)
608 2715ade4 Sofia Papagiannaki
609 1f96b68d Sofia Papagiannaki
    @debug_method
610 5d022141 Sofia Papagiannaki
    @backend_method
611 29148653 Sofia Papagiannaki
    def get_container_meta(self, user, account, container, domain, until=None,
612 29148653 Sofia Papagiannaki
                           include_user_defined=True):
613 cb69c154 Antony Chazapis
        """Return a dictionary with the container metadata for the domain."""
614 2715ade4 Sofia Papagiannaki
615 ebdbac7a Sofia Papagiannaki
        self._can_read_container(user, account, container)
616 a9b3f29d Antony Chazapis
        if user != account:
617 ebdbac7a Sofia Papagiannaki
            if until:
618 a9b3f29d Antony Chazapis
                raise NotAllowedError
619 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
620 c915d3bf Antony Chazapis
        props = self._get_properties(node, until)
621 2c5363a0 Antony Chazapis
        mtime = props[self.MTIME]
622 62f915a1 Antony Chazapis
        count, bytes, tstamp = self._get_statistics(node, until)
623 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
624 a9b3f29d Antony Chazapis
        if until is None:
625 a9b3f29d Antony Chazapis
            modified = tstamp
626 a9b3f29d Antony Chazapis
        else:
627 2715ade4 Sofia Papagiannaki
            modified = self._get_statistics(
628 2715ade4 Sofia Papagiannaki
                node)[2]  # Overall last modification.
629 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
630 2715ade4 Sofia Papagiannaki
631 a9b3f29d Antony Chazapis
        if user != account:
632 c915d3bf Antony Chazapis
            meta = {'name': container}
633 a9b3f29d Antony Chazapis
        else:
634 82482e2c Antony Chazapis
            meta = {}
635 82482e2c Antony Chazapis
            if include_user_defined:
636 2715ade4 Sofia Papagiannaki
                meta.update(
637 2715ade4 Sofia Papagiannaki
                    dict(self.node.attribute_get(props[self.SERIAL], domain)))
638 a9b3f29d Antony Chazapis
            if until is not None:
639 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
640 c915d3bf Antony Chazapis
            meta.update({'name': container, 'count': count, 'bytes': bytes})
641 c915d3bf Antony Chazapis
        meta.update({'modified': modified})
642 a9b3f29d Antony Chazapis
        return meta
643 2715ade4 Sofia Papagiannaki
644 1f96b68d Sofia Papagiannaki
    @debug_method
645 5d022141 Sofia Papagiannaki
    @backend_method
646 29148653 Sofia Papagiannaki
    def update_container_meta(self, user, account, container, domain, meta,
647 29148653 Sofia Papagiannaki
                              replace=False):
648 cb69c154 Antony Chazapis
        """Update the metadata associated with the container for the domain."""
649 2715ade4 Sofia Papagiannaki
650 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
651 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
652 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
653 0f510652 Sofia Papagiannaki
            user, node, domain, meta, replace,
654 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=0)
655 f9ea264b Antony Chazapis
        if src_version_id is not None:
656 19ddd41b Sofia Papagiannaki
            versioning = self._get_policy(
657 19ddd41b Sofia Papagiannaki
                node, is_account_policy=False)['versioning']
658 f9ea264b Antony Chazapis
            if versioning != 'auto':
659 0f510652 Sofia Papagiannaki
                self.node.version_remove(src_version_id,
660 0f510652 Sofia Papagiannaki
                                         update_statistics_ancestors_depth=0)
661 2715ade4 Sofia Papagiannaki
662 1f96b68d Sofia Papagiannaki
    @debug_method
663 5d022141 Sofia Papagiannaki
    @backend_method
664 a9b3f29d Antony Chazapis
    def get_container_policy(self, user, account, container):
665 a9b3f29d Antony Chazapis
        """Return a dictionary with the container policy."""
666 2715ade4 Sofia Papagiannaki
667 ebdbac7a Sofia Papagiannaki
        self._can_read_container(user, account, container)
668 a9b3f29d Antony Chazapis
        if user != account:
669 a9b3f29d Antony Chazapis
            return {}
670 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
671 19ddd41b Sofia Papagiannaki
        return self._get_policy(node, is_account_policy=False)
672 2715ade4 Sofia Papagiannaki
673 1f96b68d Sofia Papagiannaki
    @debug_method
674 5d022141 Sofia Papagiannaki
    @backend_method
675 29148653 Sofia Papagiannaki
    def update_container_policy(self, user, account, container, policy,
676 29148653 Sofia Papagiannaki
                                replace=False):
677 b2832c6a Antony Chazapis
        """Update the policy associated with the container."""
678 2715ade4 Sofia Papagiannaki
679 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
680 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
681 19ddd41b Sofia Papagiannaki
        self._check_policy(policy, is_account_policy=False)
682 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, replace, is_account_policy=False)
683 2715ade4 Sofia Papagiannaki
684 1f96b68d Sofia Papagiannaki
    @debug_method
685 5d022141 Sofia Papagiannaki
    @backend_method
686 78348987 Sofia Papagiannaki
    def put_container(self, user, account, container, policy=None):
687 a9b3f29d Antony Chazapis
        """Create a new container with the given name."""
688 2715ade4 Sofia Papagiannaki
689 78348987 Sofia Papagiannaki
        policy = policy or {}
690 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
691 a9b3f29d Antony Chazapis
        try:
692 c915d3bf Antony Chazapis
            path, node = self._lookup_container(account, container)
693 a9b3f29d Antony Chazapis
        except NameError:
694 a9b3f29d Antony Chazapis
            pass
695 a9b3f29d Antony Chazapis
        else:
696 7efc9f86 Sofia Papagiannaki
            raise ContainerExists('Container already exists')
697 a9b3f29d Antony Chazapis
        if policy:
698 19ddd41b Sofia Papagiannaki
            self._check_policy(policy, is_account_policy=False)
699 a9b3f29d Antony Chazapis
        path = '/'.join((account, container))
700 2715ade4 Sofia Papagiannaki
        node = self._put_path(
701 0f510652 Sofia Papagiannaki
            user, self._lookup_account(account, True)[1], path,
702 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=-1)
703 19ddd41b Sofia Papagiannaki
        self._put_policy(node, policy, True, is_account_policy=False)
704 2715ade4 Sofia Papagiannaki
705 1f96b68d Sofia Papagiannaki
    @debug_method
706 5d022141 Sofia Papagiannaki
    @backend_method
707 29148653 Sofia Papagiannaki
    def delete_container(self, user, account, container, until=None, prefix='',
708 29148653 Sofia Papagiannaki
                         delimiter=None):
709 a9b3f29d Antony Chazapis
        """Delete/purge the container with the given name."""
710 2715ade4 Sofia Papagiannaki
711 ebdbac7a Sofia Papagiannaki
        self._can_write_container(user, account, container)
712 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
713 2715ade4 Sofia Papagiannaki
714 a9b3f29d Antony Chazapis
        if until is not None:
715 388ea25f Sofia Papagiannaki
            hashes, size, serials = self.node.node_purge_children(
716 0f510652 Sofia Papagiannaki
                node, until, CLUSTER_HISTORY,
717 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=0)
718 04230536 Antony Chazapis
            for h in hashes:
719 04230536 Antony Chazapis
                self.store.map_delete(h)
720 0f510652 Sofia Papagiannaki
            self.node.node_purge_children(node, until, CLUSTER_DELETED,
721 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=0)
722 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
723 0a92ff85 Sofia Papagiannaki
                self._report_size_change(
724 0a92ff85 Sofia Papagiannaki
                    user, account, -size, {
725 29148653 Sofia Papagiannaki
                        'action': 'container purge',
726 0a92ff85 Sofia Papagiannaki
                        'path': path,
727 0a92ff85 Sofia Papagiannaki
                        'versions': ','.join(str(i) for i in serials)
728 0a92ff85 Sofia Papagiannaki
                    }
729 0a92ff85 Sofia Papagiannaki
                )
730 a9b3f29d Antony Chazapis
            return
731 2715ade4 Sofia Papagiannaki
732 e46b2bcf Sofia Papagiannaki
        if not delimiter:
733 e46b2bcf Sofia Papagiannaki
            if self._get_statistics(node)[0] > 0:
734 e46b2bcf Sofia Papagiannaki
                raise ContainerNotEmpty('Container is not empty')
735 388ea25f Sofia Papagiannaki
            hashes, size, serials = self.node.node_purge_children(
736 0f510652 Sofia Papagiannaki
                node, inf, CLUSTER_HISTORY,
737 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=0)
738 e46b2bcf Sofia Papagiannaki
            for h in hashes:
739 e46b2bcf Sofia Papagiannaki
                self.store.map_delete(h)
740 0f510652 Sofia Papagiannaki
            self.node.node_purge_children(node, inf, CLUSTER_DELETED,
741 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=0)
742 0f510652 Sofia Papagiannaki
            self.node.node_remove(node, update_statistics_ancestors_depth=0)
743 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
744 0a92ff85 Sofia Papagiannaki
                self._report_size_change(
745 0a92ff85 Sofia Papagiannaki
                    user, account, -size, {
746 29148653 Sofia Papagiannaki
                        'action': 'container purge',
747 0a92ff85 Sofia Papagiannaki
                        'path': path,
748 0a92ff85 Sofia Papagiannaki
                        'versions': ','.join(str(i) for i in serials)
749 0a92ff85 Sofia Papagiannaki
                    }
750 0a92ff85 Sofia Papagiannaki
                )
751 e46b2bcf Sofia Papagiannaki
        else:
752 b1dadd0e Sofia Papagiannaki
            # remove only contents
753 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
754 29148653 Sofia Papagiannaki
                user, account, container, prefix='', delimiter=None,
755 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
756 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
757 e46b2bcf Sofia Papagiannaki
            paths = []
758 e46b2bcf Sofia Papagiannaki
            for t in src_names:
759 e46b2bcf Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
760 e46b2bcf Sofia Papagiannaki
                node = t[2]
761 33af031c Sofia Papagiannaki
                if not self._exists(node):
762 33af031c Sofia Papagiannaki
                    continue
763 0f510652 Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(
764 0f510652 Sofia Papagiannaki
                    user, node, size=0, type='', hash=None, checksum='',
765 0f510652 Sofia Papagiannaki
                    cluster=CLUSTER_DELETED,
766 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
767 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
768 0f510652 Sofia Papagiannaki
                    account, container, src_version_id,
769 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
770 b1dadd0e Sofia Papagiannaki
                self._report_size_change(
771 29148653 Sofia Papagiannaki
                    user, account, -del_size, {
772 29148653 Sofia Papagiannaki
                        'action': 'object delete',
773 29148653 Sofia Papagiannaki
                        'path': path,
774 29148653 Sofia Papagiannaki
                        'versions': ','.join([str(dest_version_id)])})
775 2715ade4 Sofia Papagiannaki
                self._report_object_change(
776 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
777 e46b2bcf Sofia Papagiannaki
                paths.append(path)
778 e46b2bcf Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
779 2715ade4 Sofia Papagiannaki
780 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
781 ebdbac7a Sofia Papagiannaki
        # removing the specific path could be more expensive
782 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
783 ebdbac7a Sofia Papagiannaki
784 29148653 Sofia Papagiannaki
    def _list_objects(self, user, account, container, prefix, delimiter,
785 29148653 Sofia Papagiannaki
                      marker, limit, virtual, domain, keys, shared, until,
786 29148653 Sofia Papagiannaki
                      size_range, all_props, public):
787 15a96c3e Antony Chazapis
        if user != account and until:
788 15a96c3e Antony Chazapis
            raise NotAllowedError
789 9c6ddb46 Sofia Papagiannaki
790 9c6ddb46 Sofia Papagiannaki
        objects = []
791 cf4a7a7b Sofia Papagiannaki
        if shared and public:
792 cf4a7a7b Sofia Papagiannaki
            # get shared first
793 5576e6dd Sofia Papagiannaki
            shared_paths = self._list_object_permissions(
794 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, shared=True, public=False)
795 5576e6dd Sofia Papagiannaki
            if shared_paths:
796 cf4a7a7b Sofia Papagiannaki
                path, node = self._lookup_container(account, container)
797 5576e6dd Sofia Papagiannaki
                shared_paths = self._get_formatted_paths(shared_paths)
798 9c6ddb46 Sofia Papagiannaki
                objects = set(self._list_object_properties(
799 29148653 Sofia Papagiannaki
                    node, path, prefix, delimiter, marker, limit, virtual,
800 29148653 Sofia Papagiannaki
                    domain, keys, until, size_range, shared_paths, all_props))
801 2715ade4 Sofia Papagiannaki
802 cf4a7a7b Sofia Papagiannaki
            # get public
803 2715ade4 Sofia Papagiannaki
            objects |= set(self._list_public_object_properties(
804 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, all_props))
805 31e1acd3 Sofia Papagiannaki
            objects = list(objects)
806 2715ade4 Sofia Papagiannaki
807 cf4a7a7b Sofia Papagiannaki
            objects.sort(key=lambda x: x[0])
808 cf4a7a7b Sofia Papagiannaki
        elif public:
809 2715ade4 Sofia Papagiannaki
            objects = self._list_public_object_properties(
810 2715ade4 Sofia Papagiannaki
                user, account, container, prefix, all_props)
811 9c6ddb46 Sofia Papagiannaki
        else:
812 9c6ddb46 Sofia Papagiannaki
            allowed = self._list_object_permissions(
813 9c6ddb46 Sofia Papagiannaki
                user, account, container, prefix, shared, public=False)
814 9c6ddb46 Sofia Papagiannaki
            if shared and not allowed:
815 9c6ddb46 Sofia Papagiannaki
                return []
816 9c6ddb46 Sofia Papagiannaki
            path, node = self._lookup_container(account, container)
817 9c6ddb46 Sofia Papagiannaki
            allowed = self._get_formatted_paths(allowed)
818 9c6ddb46 Sofia Papagiannaki
            objects = self._list_object_properties(
819 9c6ddb46 Sofia Papagiannaki
                node, path, prefix, delimiter, marker, limit, virtual, domain,
820 9c6ddb46 Sofia Papagiannaki
                keys, until, size_range, allowed, all_props)
821 9c6ddb46 Sofia Papagiannaki
822 9c6ddb46 Sofia Papagiannaki
        # apply limits
823 9c6ddb46 Sofia Papagiannaki
        start, limit = self._list_limits(objects, marker, limit)
824 cf4a7a7b Sofia Papagiannaki
        return objects[start:start + limit]
825 2715ade4 Sofia Papagiannaki
826 29148653 Sofia Papagiannaki
    def _list_public_object_properties(self, user, account, container, prefix,
827 29148653 Sofia Papagiannaki
                                       all_props):
828 2715ade4 Sofia Papagiannaki
        public = self._list_object_permissions(
829 2715ade4 Sofia Papagiannaki
            user, account, container, prefix, shared=False, public=True)
830 cf4a7a7b Sofia Papagiannaki
        paths, nodes = self._lookup_objects(public)
831 cf4a7a7b Sofia Papagiannaki
        path = '/'.join((account, container))
832 cf4a7a7b Sofia Papagiannaki
        cont_prefix = path + '/'
833 cf4a7a7b Sofia Papagiannaki
        paths = [x[len(cont_prefix):] for x in paths]
834 29148653 Sofia Papagiannaki
        objects = [(p,) + props for p, props in
835 29148653 Sofia Papagiannaki
                   zip(paths, self.node.version_lookup_bulk(
836 ba402621 Sofia Papagiannaki
                       nodes, all_props=all_props, order_by_path=True))]
837 cf4a7a7b Sofia Papagiannaki
        return objects
838 2715ade4 Sofia Papagiannaki
839 29148653 Sofia Papagiannaki
    def _list_objects_no_limit(self, user, account, container, prefix,
840 29148653 Sofia Papagiannaki
                               delimiter, virtual, domain, keys, shared, until,
841 29148653 Sofia Papagiannaki
                               size_range, all_props, public):
842 4d15c94e Sofia Papagiannaki
        objects = []
843 4d15c94e Sofia Papagiannaki
        while True:
844 4d15c94e Sofia Papagiannaki
            marker = objects[-1] if objects else None
845 4d15c94e Sofia Papagiannaki
            limit = 10000
846 29148653 Sofia Papagiannaki
            l = self._list_objects(
847 29148653 Sofia Papagiannaki
                user, account, container, prefix, delimiter, marker, limit,
848 29148653 Sofia Papagiannaki
                virtual, domain, keys, shared, until, size_range, all_props,
849 29148653 Sofia Papagiannaki
                public)
850 4d15c94e Sofia Papagiannaki
            objects.extend(l)
851 4d15c94e Sofia Papagiannaki
            if not l or len(l) < limit:
852 4d15c94e Sofia Papagiannaki
                break
853 4d15c94e Sofia Papagiannaki
        return objects
854 2715ade4 Sofia Papagiannaki
855 29148653 Sofia Papagiannaki
    def _list_object_permissions(self, user, account, container, prefix,
856 29148653 Sofia Papagiannaki
                                 shared, public):
857 62f915a1 Antony Chazapis
        allowed = []
858 fcd37c40 Antony Chazapis
        path = '/'.join((account, container, prefix)).rstrip('/')
859 62f915a1 Antony Chazapis
        if user != account:
860 fcd37c40 Antony Chazapis
            allowed = self.permissions.access_list_paths(user, path)
861 62f915a1 Antony Chazapis
            if not allowed:
862 62f915a1 Antony Chazapis
                raise NotAllowedError
863 62f915a1 Antony Chazapis
        else:
864 56ac7c81 Sofia Papagiannaki
            allowed = set()
865 62f915a1 Antony Chazapis
            if shared:
866 56ac7c81 Sofia Papagiannaki
                allowed.update(self.permissions.access_list_shared(path))
867 c53c4def Sofia Papagiannaki
            if public:
868 2715ade4 Sofia Papagiannaki
                allowed.update(
869 2715ade4 Sofia Papagiannaki
                    [x[0] for x in self.permissions.public_list(path)])
870 56ac7c81 Sofia Papagiannaki
            allowed = sorted(allowed)
871 c53c4def Sofia Papagiannaki
            if not allowed:
872 c53c4def Sofia Papagiannaki
                return []
873 15a96c3e Antony Chazapis
        return allowed
874 2715ade4 Sofia Papagiannaki
875 1f96b68d Sofia Papagiannaki
    @debug_method
876 5d022141 Sofia Papagiannaki
    @backend_method
877 29148653 Sofia Papagiannaki
    def list_objects(self, user, account, container, prefix='', delimiter=None,
878 29148653 Sofia Papagiannaki
                     marker=None, limit=10000, virtual=True, domain=None,
879 29148653 Sofia Papagiannaki
                     keys=None, shared=False, until=None, size_range=None,
880 29148653 Sofia Papagiannaki
                     public=False):
881 29148653 Sofia Papagiannaki
        """List (object name, object version_id) under a container."""
882 2715ade4 Sofia Papagiannaki
883 78348987 Sofia Papagiannaki
        keys = keys or []
884 29148653 Sofia Papagiannaki
        return self._list_objects(
885 29148653 Sofia Papagiannaki
            user, account, container, prefix, delimiter, marker, limit,
886 29148653 Sofia Papagiannaki
            virtual, domain, keys, shared, until, size_range, False, public)
887 2715ade4 Sofia Papagiannaki
888 1f96b68d Sofia Papagiannaki
    @debug_method
889 5d022141 Sofia Papagiannaki
    @backend_method
890 29148653 Sofia Papagiannaki
    def list_object_meta(self, user, account, container, prefix='',
891 29148653 Sofia Papagiannaki
                         delimiter=None, marker=None, limit=10000,
892 29148653 Sofia Papagiannaki
                         virtual=True, domain=None, keys=None, shared=False,
893 29148653 Sofia Papagiannaki
                         until=None, size_range=None, public=False):
894 29148653 Sofia Papagiannaki
        """Return a list of metadata dicts of objects under a container."""
895 2715ade4 Sofia Papagiannaki
896 78348987 Sofia Papagiannaki
        keys = keys or []
897 29148653 Sofia Papagiannaki
        props = self._list_objects(
898 29148653 Sofia Papagiannaki
            user, account, container, prefix, delimiter, marker, limit,
899 29148653 Sofia Papagiannaki
            virtual, domain, keys, shared, until, size_range, True, public)
900 371d907a Antony Chazapis
        objects = []
901 371d907a Antony Chazapis
        for p in props:
902 371d907a Antony Chazapis
            if len(p) == 2:
903 371d907a Antony Chazapis
                objects.append({'subdir': p[0]})
904 371d907a Antony Chazapis
            else:
905 29148653 Sofia Papagiannaki
                objects.append({
906 29148653 Sofia Papagiannaki
                    'name': p[0],
907 29148653 Sofia Papagiannaki
                    'bytes': p[self.SIZE + 1],
908 29148653 Sofia Papagiannaki
                    'type': p[self.TYPE + 1],
909 29148653 Sofia Papagiannaki
                    'hash': p[self.HASH + 1],
910 29148653 Sofia Papagiannaki
                    'version': p[self.SERIAL + 1],
911 29148653 Sofia Papagiannaki
                    'version_timestamp': p[self.MTIME + 1],
912 29148653 Sofia Papagiannaki
                    'modified': p[self.MTIME + 1] if until is None else None,
913 29148653 Sofia Papagiannaki
                    'modified_by': p[self.MUSER + 1],
914 29148653 Sofia Papagiannaki
                    'uuid': p[self.UUID + 1],
915 6e3e5c84 Sofia Papagiannaki
                    'checksum': p[self.CHECKSUM + 1],
916 6e3e5c84 Sofia Papagiannaki
                    'available': p[self.AVAILABLE + 1],
917 6e3e5c84 Sofia Papagiannaki
                    'map_check_timestamp': p[self.MAP_CHECK_TIMESTAMP + 1]})
918 371d907a Antony Chazapis
        return objects
919 2715ade4 Sofia Papagiannaki
920 1f96b68d Sofia Papagiannaki
    @debug_method
921 5d022141 Sofia Papagiannaki
    @backend_method
922 15a96c3e Antony Chazapis
    def list_object_permissions(self, user, account, container, prefix=''):
923 29148653 Sofia Papagiannaki
        """Return a list of paths enforce permissions under a container."""
924 2715ade4 Sofia Papagiannaki
925 29148653 Sofia Papagiannaki
        return self._list_object_permissions(user, account, container, prefix,
926 29148653 Sofia Papagiannaki
                                             True, False)
927 2715ade4 Sofia Papagiannaki
928 1f96b68d Sofia Papagiannaki
    @debug_method
929 5d022141 Sofia Papagiannaki
    @backend_method
930 15a96c3e Antony Chazapis
    def list_object_public(self, user, account, container, prefix=''):
931 29148653 Sofia Papagiannaki
        """Return a mapping of object paths to public ids under a container."""
932 2715ade4 Sofia Papagiannaki
933 15a96c3e Antony Chazapis
        public = {}
934 29148653 Sofia Papagiannaki
        for path, p in self.permissions.public_list('/'.join((account,
935 29148653 Sofia Papagiannaki
                                                              container,
936 29148653 Sofia Papagiannaki
                                                              prefix))):
937 56f3c759 Sofia Papagiannaki
            public[path] = p
938 15a96c3e Antony Chazapis
        return public
939 2715ade4 Sofia Papagiannaki
940 1f96b68d Sofia Papagiannaki
    @debug_method
941 5d022141 Sofia Papagiannaki
    @backend_method
942 29148653 Sofia Papagiannaki
    def get_object_meta(self, user, account, container, name, domain,
943 29148653 Sofia Papagiannaki
                        version=None, include_user_defined=True):
944 cb69c154 Antony Chazapis
        """Return a dictionary with the object metadata for the domain."""
945 2715ade4 Sofia Papagiannaki
946 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
947 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
948 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
949 a9b3f29d Antony Chazapis
        if version is None:
950 e0525d86 Sofia Papagiannaki
            if not props[self.AVAILABLE]:
951 e0525d86 Sofia Papagiannaki
                try:
952 e0525d86 Sofia Papagiannaki
                    self._update_available(props)
953 e0525d86 Sofia Papagiannaki
                except (NotAllowedError, IllegalOperationError):
954 e0525d86 Sofia Papagiannaki
                    pass  # just update the database
955 e0525d86 Sofia Papagiannaki
                finally:
956 e0525d86 Sofia Papagiannaki
                    # get updated properties
957 e0525d86 Sofia Papagiannaki
                    props = self._get_version(node, version)
958 2c5363a0 Antony Chazapis
            modified = props[self.MTIME]
959 a9b3f29d Antony Chazapis
        else:
960 97d45f69 Antony Chazapis
            try:
961 2715ade4 Sofia Papagiannaki
                modified = self._get_version(
962 2715ade4 Sofia Papagiannaki
                    node)[self.MTIME]  # Overall last modification.
963 2715ade4 Sofia Papagiannaki
            except NameError:  # Object may be deleted.
964 2715ade4 Sofia Papagiannaki
                del_props = self.node.version_lookup(
965 2715ade4 Sofia Papagiannaki
                    node, inf, CLUSTER_DELETED)
966 97d45f69 Antony Chazapis
                if del_props is None:
967 7efc9f86 Sofia Papagiannaki
                    raise ItemNotExists('Object does not exist')
968 97d45f69 Antony Chazapis
                modified = del_props[self.MTIME]
969 2715ade4 Sofia Papagiannaki
970 82482e2c Antony Chazapis
        meta = {}
971 82482e2c Antony Chazapis
        if include_user_defined:
972 2715ade4 Sofia Papagiannaki
            meta.update(
973 2715ade4 Sofia Papagiannaki
                dict(self.node.attribute_get(props[self.SERIAL], domain)))
974 33b4e4a6 Antony Chazapis
        meta.update({'name': name,
975 33b4e4a6 Antony Chazapis
                     'bytes': props[self.SIZE],
976 33b4e4a6 Antony Chazapis
                     'type': props[self.TYPE],
977 371d907a Antony Chazapis
                     'hash': props[self.HASH],
978 33b4e4a6 Antony Chazapis
                     'version': props[self.SERIAL],
979 33b4e4a6 Antony Chazapis
                     'version_timestamp': props[self.MTIME],
980 33b4e4a6 Antony Chazapis
                     'modified': modified,
981 33b4e4a6 Antony Chazapis
                     'modified_by': props[self.MUSER],
982 33b4e4a6 Antony Chazapis
                     'uuid': props[self.UUID],
983 876d7486 Sofia Papagiannaki
                     'checksum': props[self.CHECKSUM],
984 876d7486 Sofia Papagiannaki
                     'available': props[self.AVAILABLE],
985 876d7486 Sofia Papagiannaki
                     'map_check_timestamp': props[self.MAP_CHECK_TIMESTAMP]})
986 a9b3f29d Antony Chazapis
        return meta
987 2715ade4 Sofia Papagiannaki
988 1f96b68d Sofia Papagiannaki
    @debug_method
989 5d022141 Sofia Papagiannaki
    @backend_method
990 29148653 Sofia Papagiannaki
    def update_object_meta(self, user, account, container, name, domain, meta,
991 29148653 Sofia Papagiannaki
                           replace=False):
992 29148653 Sofia Papagiannaki
        """Update object metadata for a domain and return the new version."""
993 2715ade4 Sofia Papagiannaki
994 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
995 48bb9c89 Sofia Papagiannaki
996 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
997 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
998 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
999 0f510652 Sofia Papagiannaki
            user, node, domain, meta, replace,
1000 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=1)
1001 0f510652 Sofia Papagiannaki
        self._apply_versioning(account, container, src_version_id,
1002 0f510652 Sofia Papagiannaki
                               update_statistics_ancestors_depth=1)
1003 5cc484e1 Antony Chazapis
        return dest_version_id
1004 62e6d12e Nanakos Chrysostomos
1005 dc88754b Nanakos Chrysostomos
    @debug_method
1006 5d022141 Sofia Papagiannaki
    @backend_method
1007 dc88754b Nanakos Chrysostomos
    def get_object_permissions_bulk(self, user, account, container, names):
1008 dc88754b Nanakos Chrysostomos
        """Return the action allowed on the object, the path
1009 dc88754b Nanakos Chrysostomos
        from which the object gets its permissions from,
1010 dc88754b Nanakos Chrysostomos
        along with a dictionary containing the permissions."""
1011 dc88754b Nanakos Chrysostomos
1012 ace7592b Sofia Papagiannaki
        permissions_path = self._get_permissions_path_bulk(account, container,
1013 ace7592b Sofia Papagiannaki
                                                           names)
1014 dc88754b Nanakos Chrysostomos
        access_objects = self.permissions.access_check_bulk(permissions_path,
1015 ace7592b Sofia Papagiannaki
                                                            user)
1016 62e6d12e Nanakos Chrysostomos
        #group_parents = access_objects['group_parents']
1017 dc88754b Nanakos Chrysostomos
        nobject_permissions = {}
1018 e161c24f Sofia Papagiannaki
        cpath = '/'.join((account, container, ''))
1019 e161c24f Sofia Papagiannaki
        cpath_idx = len(cpath)
1020 dc88754b Nanakos Chrysostomos
        for path in permissions_path:
1021 dc88754b Nanakos Chrysostomos
            allowed = 1
1022 e161c24f Sofia Papagiannaki
            name = path[cpath_idx:]
1023 dc88754b Nanakos Chrysostomos
            if user != account:
1024 dc88754b Nanakos Chrysostomos
                try:
1025 dc88754b Nanakos Chrysostomos
                    allowed = access_objects[path]
1026 dc88754b Nanakos Chrysostomos
                except KeyError:
1027 dc88754b Nanakos Chrysostomos
                    raise NotAllowedError
1028 dc88754b Nanakos Chrysostomos
            access_dict, allowed = \
1029 dc88754b Nanakos Chrysostomos
                self.permissions.access_get_for_bulk(access_objects[path])
1030 dc88754b Nanakos Chrysostomos
            nobject_permissions[name] = (self.ALLOWED[allowed], path,
1031 dc88754b Nanakos Chrysostomos
                                         access_dict)
1032 dc88754b Nanakos Chrysostomos
        self._lookup_objects(permissions_path)
1033 dc88754b Nanakos Chrysostomos
        return nobject_permissions
1034 dc88754b Nanakos Chrysostomos
1035 1f96b68d Sofia Papagiannaki
    @debug_method
1036 5d022141 Sofia Papagiannaki
    @backend_method
1037 a9b3f29d Antony Chazapis
    def get_object_permissions(self, user, account, container, name):
1038 067cf1fc Antony Chazapis
        """Return the action allowed on the object, the path
1039 067cf1fc Antony Chazapis
        from which the object gets its permissions from,
1040 a9b3f29d Antony Chazapis
        along with a dictionary containing the permissions."""
1041 2715ade4 Sofia Papagiannaki
1042 067cf1fc Antony Chazapis
        allowed = 'write'
1043 92da0e5a Antony Chazapis
        permissions_path = self._get_permissions_path(account, container, name)
1044 067cf1fc Antony Chazapis
        if user != account:
1045 29148653 Sofia Papagiannaki
            if self.permissions.access_check(permissions_path, self.WRITE,
1046 29148653 Sofia Papagiannaki
                                             user):
1047 067cf1fc Antony Chazapis
                allowed = 'write'
1048 29148653 Sofia Papagiannaki
            elif self.permissions.access_check(permissions_path, self.READ,
1049 29148653 Sofia Papagiannaki
                                               user):
1050 067cf1fc Antony Chazapis
                allowed = 'read'
1051 067cf1fc Antony Chazapis
            else:
1052 067cf1fc Antony Chazapis
                raise NotAllowedError
1053 92da0e5a Antony Chazapis
        self._lookup_object(account, container, name)
1054 29148653 Sofia Papagiannaki
        return (allowed,
1055 29148653 Sofia Papagiannaki
                permissions_path,
1056 29148653 Sofia Papagiannaki
                self.permissions.access_get(permissions_path))
1057 2715ade4 Sofia Papagiannaki
1058 1f96b68d Sofia Papagiannaki
    @debug_method
1059 5d022141 Sofia Papagiannaki
    @backend_method
1060 29148653 Sofia Papagiannaki
    def update_object_permissions(self, user, account, container, name,
1061 29148653 Sofia Papagiannaki
                                  permissions):
1062 a9b3f29d Antony Chazapis
        """Update the permissions associated with the object."""
1063 2715ade4 Sofia Papagiannaki
1064 a9b3f29d Antony Chazapis
        if user != account:
1065 a9b3f29d Antony Chazapis
            raise NotAllowedError
1066 48bb9c89 Sofia Papagiannaki
        path = self._lookup_object(account, container, name,
1067 48bb9c89 Sofia Papagiannaki
                                   lock_container=True)[0]
1068 6f4bce7b Antony Chazapis
        self._check_permissions(path, permissions)
1069 6c9df07c Sofia Papagiannaki
        try:
1070 6c9df07c Sofia Papagiannaki
            self.permissions.access_set(path, permissions)
1071 6c9df07c Sofia Papagiannaki
        except:
1072 6c9df07c Sofia Papagiannaki
            raise ValueError
1073 6c9df07c Sofia Papagiannaki
        else:
1074 6c9df07c Sofia Papagiannaki
            self._report_sharing_change(user, account, path, {'members':
1075 6c9df07c Sofia Papagiannaki
                                        self.permissions.access_members(path)})
1076 2715ade4 Sofia Papagiannaki
1077 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
1078 ebdbac7a Sofia Papagiannaki
        # filtering out only those affected could be more expensive
1079 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
1080 ebdbac7a Sofia Papagiannaki
1081 1f96b68d Sofia Papagiannaki
    @debug_method
1082 5d022141 Sofia Papagiannaki
    @backend_method
1083 a9b3f29d Antony Chazapis
    def get_object_public(self, user, account, container, name):
1084 bb4eafc6 Antony Chazapis
        """Return the public id of the object if applicable."""
1085 2715ade4 Sofia Papagiannaki
1086 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1087 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
1088 bb4eafc6 Antony Chazapis
        p = self.permissions.public_get(path)
1089 bb4eafc6 Antony Chazapis
        return p
1090 2715ade4 Sofia Papagiannaki
1091 1f96b68d Sofia Papagiannaki
    @debug_method
1092 5d022141 Sofia Papagiannaki
    @backend_method
1093 a9b3f29d Antony Chazapis
    def update_object_public(self, user, account, container, name, public):
1094 a9b3f29d Antony Chazapis
        """Update the public status of the object."""
1095 2715ade4 Sofia Papagiannaki
1096 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
1097 48bb9c89 Sofia Papagiannaki
        path = self._lookup_object(account, container, name,
1098 48bb9c89 Sofia Papagiannaki
                                   lock_container=True)[0]
1099 0f9d752c Antony Chazapis
        if not public:
1100 0f9d752c Antony Chazapis
            self.permissions.public_unset(path)
1101 0f9d752c Antony Chazapis
        else:
1102 56f3c759 Sofia Papagiannaki
            self.permissions.public_set(
1103 adce84cd Sofia Papagiannaki
                path, self.public_url_security, self.public_url_alphabet)
1104 2715ade4 Sofia Papagiannaki
1105 876d7486 Sofia Papagiannaki
    def _update_available(self, props):
1106 876d7486 Sofia Papagiannaki
        """Checks if the object map exists and updates the database"""
1107 876d7486 Sofia Papagiannaki
1108 876d7486 Sofia Papagiannaki
        if not props[self.AVAILABLE]:
1109 876d7486 Sofia Papagiannaki
            if props[self.MAP_CHECK_TIMESTAMP]:
1110 876d7486 Sofia Papagiannaki
                elapsed_time = time() - float(props[self.MAP_CHECK_TIMESTAMP])
1111 876d7486 Sofia Papagiannaki
                if elapsed_time < self.map_check_interval:
1112 876d7486 Sofia Papagiannaki
                    raise NotAllowedError(
1113 876d7486 Sofia Papagiannaki
                        'Consequent map checks are limited: retry later.')
1114 876d7486 Sofia Papagiannaki
        try:
1115 876d7486 Sofia Papagiannaki
            hashmap = self.store.map_get_archipelago(props[self.HASH],
1116 876d7486 Sofia Papagiannaki
                                                     props[self.SIZE])
1117 876d7486 Sofia Papagiannaki
        except:  # map does not exist
1118 876d7486 Sofia Papagiannaki
            # Raising an exception results in db transaction rollback
1119 876d7486 Sofia Papagiannaki
            # However we have to force the update of the database
1120 876d7486 Sofia Papagiannaki
            self.wrapper.rollback()  # rollback existing transaction
1121 876d7486 Sofia Papagiannaki
            self.wrapper.execute()  # start new transaction
1122 876d7486 Sofia Papagiannaki
            self.node.version_put_property(props[self.SERIAL],
1123 876d7486 Sofia Papagiannaki
                                           'map_check_timestamp', time())
1124 876d7486 Sofia Papagiannaki
            self.wrapper.commit()  # commit transaction
1125 876d7486 Sofia Papagiannaki
            self.wrapper.execute()  # start new transaction
1126 876d7486 Sofia Papagiannaki
            raise IllegalOperationError(
1127 876d7486 Sofia Papagiannaki
                'Unable to retrieve Archipelago Volume hashmap.')
1128 876d7486 Sofia Papagiannaki
        else:  # map exists
1129 876d7486 Sofia Papagiannaki
            self.node.version_put_property(props[self.SERIAL],
1130 876d7486 Sofia Papagiannaki
                                           'available', True)
1131 876d7486 Sofia Papagiannaki
            self.node.version_put_property(props[self.SERIAL],
1132 876d7486 Sofia Papagiannaki
                                           'map_check_timestamp', time())
1133 876d7486 Sofia Papagiannaki
            return hashmap
1134 876d7486 Sofia Papagiannaki
1135 1f96b68d Sofia Papagiannaki
    @debug_method
1136 5d022141 Sofia Papagiannaki
    @backend_method
1137 a9b3f29d Antony Chazapis
    def get_object_hashmap(self, user, account, container, name, version=None):
1138 a9b3f29d Antony Chazapis
        """Return the object's size and a list with partial hashes."""
1139 2715ade4 Sofia Papagiannaki
1140 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1141 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1142 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
1143 adce84cd Sofia Papagiannaki
        if props[self.HASH] is None:
1144 adce84cd Sofia Papagiannaki
            return 0, ()
1145 1e47e49d Chrysostomos Nanakos
        if props[self.HASH].startswith('archip:'):
1146 876d7486 Sofia Papagiannaki
            hashmap = self._update_available(props)
1147 1e47e49d Chrysostomos Nanakos
            return props[self.SIZE], [x for x in hashmap]
1148 1e47e49d Chrysostomos Nanakos
        else:
1149 1e47e49d Chrysostomos Nanakos
            hashmap = self.store.map_get(self._unhexlify_hash(
1150 1e47e49d Chrysostomos Nanakos
                props[self.HASH]))
1151 1e47e49d Chrysostomos Nanakos
            return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
1152 2715ade4 Sofia Papagiannaki
1153 29148653 Sofia Papagiannaki
    def _update_object_hash(self, user, account, container, name, size, type,
1154 29148653 Sofia Papagiannaki
                            hash, checksum, domain, meta, replace_meta,
1155 29148653 Sofia Papagiannaki
                            permissions, src_node=None, src_version_id=None,
1156 876d7486 Sofia Papagiannaki
                            is_copy=False, report_size_change=True,
1157 876d7486 Sofia Papagiannaki
                            available=True):
1158 b9064632 Antony Chazapis
        if permissions is not None and user != account:
1159 b9064632 Antony Chazapis
            raise NotAllowedError
1160 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
1161 b9064632 Antony Chazapis
        if permissions is not None:
1162 b9064632 Antony Chazapis
            path = '/'.join((account, container, name))
1163 b9064632 Antony Chazapis
            self._check_permissions(path, permissions)
1164 2715ade4 Sofia Papagiannaki
1165 b9064632 Antony Chazapis
        account_path, account_node = self._lookup_account(account, True)
1166 2715ade4 Sofia Papagiannaki
        container_path, container_node = self._lookup_container(
1167 2715ade4 Sofia Papagiannaki
            account, container)
1168 fed9c5c7 Sofia Papagiannaki
1169 2715ade4 Sofia Papagiannaki
        path, node = self._put_object_node(
1170 2715ade4 Sofia Papagiannaki
            container_path, container_node, name)
1171 0f510652 Sofia Papagiannaki
        pre_version_id, dest_version_id = self._put_version_duplicate(
1172 0f510652 Sofia Papagiannaki
            user, node, src_node=src_node, size=size, type=type, hash=hash,
1173 0f510652 Sofia Papagiannaki
            checksum=checksum, is_copy=is_copy,
1174 e0525d86 Sofia Papagiannaki
            update_statistics_ancestors_depth=1,
1175 e0525d86 Sofia Papagiannaki
            available=available, keep_available=False)
1176 2715ade4 Sofia Papagiannaki
1177 f9ea264b Antony Chazapis
        # Handle meta.
1178 f9ea264b Antony Chazapis
        if src_version_id is None:
1179 f9ea264b Antony Chazapis
            src_version_id = pre_version_id
1180 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1181 ad9ada51 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace_meta)
1182 2715ade4 Sofia Papagiannaki
1183 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, pre_version_id,
1184 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
1185 813e42e5 Antony Chazapis
        size_delta = size - del_size
1186 fed9c5c7 Sofia Papagiannaki
        if size_delta > 0:
1187 fed9c5c7 Sofia Papagiannaki
            # Check account quota.
1188 fed9c5c7 Sofia Papagiannaki
            if not self.using_external_quotaholder:
1189 29148653 Sofia Papagiannaki
                account_quota = long(self._get_policy(
1190 29148653 Sofia Papagiannaki
                    account_node, is_account_policy=True)['quota'])
1191 29148653 Sofia Papagiannaki
                account_usage = self._get_statistics(account_node,
1192 29148653 Sofia Papagiannaki
                                                     compute=True)[1]
1193 d3655326 Sofia Papagiannaki
                if (account_quota > 0 and account_usage > account_quota):
1194 fed9c5c7 Sofia Papagiannaki
                    raise QuotaError(
1195 fed9c5c7 Sofia Papagiannaki
                        'Account quota exceeded: limit: %s, usage: %s' % (
1196 29148653 Sofia Papagiannaki
                            account_quota, account_usage))
1197 fed9c5c7 Sofia Papagiannaki
1198 fed9c5c7 Sofia Papagiannaki
            # Check container quota.
1199 29148653 Sofia Papagiannaki
            container_quota = long(self._get_policy(
1200 29148653 Sofia Papagiannaki
                container_node, is_account_policy=False)['quota'])
1201 fed9c5c7 Sofia Papagiannaki
            container_usage = self._get_statistics(container_node)[1]
1202 fed9c5c7 Sofia Papagiannaki
            if (container_quota > 0 and container_usage > container_quota):
1203 fed9c5c7 Sofia Papagiannaki
                # This must be executed in a transaction, so the version is
1204 fed9c5c7 Sofia Papagiannaki
                # never created if it fails.
1205 fed9c5c7 Sofia Papagiannaki
                raise QuotaError(
1206 fed9c5c7 Sofia Papagiannaki
                    'Container quota exceeded: limit: %s, usage: %s' % (
1207 fed9c5c7 Sofia Papagiannaki
                        container_quota, container_usage
1208 fed9c5c7 Sofia Papagiannaki
                    )
1209 fed9c5c7 Sofia Papagiannaki
                )
1210 e20a751d Sofia Papagiannaki
1211 9e3a38bb Sofia Papagiannaki
        if report_size_change:
1212 9e3a38bb Sofia Papagiannaki
            self._report_size_change(
1213 9e3a38bb Sofia Papagiannaki
                user, account, size_delta,
1214 9e3a38bb Sofia Papagiannaki
                {'action': 'object update', 'path': path,
1215 9e3a38bb Sofia Papagiannaki
                 'versions': ','.join([str(dest_version_id)])})
1216 b9064632 Antony Chazapis
        if permissions is not None:
1217 b9064632 Antony Chazapis
            self.permissions.access_set(path, permissions)
1218 29148653 Sofia Papagiannaki
            self._report_sharing_change(
1219 29148653 Sofia Papagiannaki
                user, account, path,
1220 29148653 Sofia Papagiannaki
                {'members': self.permissions.access_members(path)})
1221 2715ade4 Sofia Papagiannaki
1222 29148653 Sofia Papagiannaki
        self._report_object_change(
1223 29148653 Sofia Papagiannaki
            user, account, path,
1224 29148653 Sofia Papagiannaki
            details={'version': dest_version_id, 'action': 'object update'})
1225 f9ea264b Antony Chazapis
        return dest_version_id
1226 2715ade4 Sofia Papagiannaki
1227 1f96b68d Sofia Papagiannaki
    @debug_method
1228 e851ad95 Chrysostomos Nanakos
    @backend_method
1229 e851ad95 Chrysostomos Nanakos
    def register_object_map(self, user, account, container, name, size, type,
1230 e851ad95 Chrysostomos Nanakos
                            mapfile, checksum='', domain='pithos', meta=None,
1231 e851ad95 Chrysostomos Nanakos
                            replace_meta=False, permissions=None):
1232 e851ad95 Chrysostomos Nanakos
        """Register an object mapfile without providing any data.
1233 e851ad95 Chrysostomos Nanakos

1234 e851ad95 Chrysostomos Nanakos
        Lock the container path, create a node pointing to the object path,
1235 e851ad95 Chrysostomos Nanakos
        create a version pointing to the mapfile
1236 e851ad95 Chrysostomos Nanakos
        and issue the size change in the quotaholder.
1237 e851ad95 Chrysostomos Nanakos

1238 e851ad95 Chrysostomos Nanakos
        :param user: the user account which performs the action
1239 e851ad95 Chrysostomos Nanakos

1240 e851ad95 Chrysostomos Nanakos
        :param account: the account under which the object resides
1241 e851ad95 Chrysostomos Nanakos

1242 e851ad95 Chrysostomos Nanakos
        :param container: the container under which the object resides
1243 e851ad95 Chrysostomos Nanakos

1244 e851ad95 Chrysostomos Nanakos
        :param name: the object name
1245 e851ad95 Chrysostomos Nanakos

1246 e851ad95 Chrysostomos Nanakos
        :param size: the object size
1247 e851ad95 Chrysostomos Nanakos

1248 e851ad95 Chrysostomos Nanakos
        :param type: the object mimetype
1249 e851ad95 Chrysostomos Nanakos

1250 e851ad95 Chrysostomos Nanakos
        :param mapfile: the mapfile pointing to the object data
1251 e851ad95 Chrysostomos Nanakos

1252 e851ad95 Chrysostomos Nanakos
        :param checkcum: the md5 checksum (optional)
1253 e851ad95 Chrysostomos Nanakos

1254 e851ad95 Chrysostomos Nanakos
        :param domain: the object domain
1255 e851ad95 Chrysostomos Nanakos

1256 e851ad95 Chrysostomos Nanakos
        :param meta: a dict with custom object metadata
1257 e851ad95 Chrysostomos Nanakos

1258 e851ad95 Chrysostomos Nanakos
        :param replace_meta: replace existing metadata or not
1259 e851ad95 Chrysostomos Nanakos

1260 e851ad95 Chrysostomos Nanakos
        :param permissions: a dict with the read and write object permissions
1261 e851ad95 Chrysostomos Nanakos

1262 e851ad95 Chrysostomos Nanakos
        :returns: the new object uuid
1263 e851ad95 Chrysostomos Nanakos

1264 e851ad95 Chrysostomos Nanakos
        :raises: ItemNotExists, NotAllowedError, QuotaError
1265 e851ad95 Chrysostomos Nanakos
        """
1266 e851ad95 Chrysostomos Nanakos
1267 e851ad95 Chrysostomos Nanakos
        meta = meta or {}
1268 e851ad95 Chrysostomos Nanakos
        try:
1269 e851ad95 Chrysostomos Nanakos
            self.lock_container_path = True
1270 e851ad95 Chrysostomos Nanakos
            self.put_container(user, account, container, policy=None)
1271 e851ad95 Chrysostomos Nanakos
        except ContainerExists:
1272 e851ad95 Chrysostomos Nanakos
            pass
1273 e851ad95 Chrysostomos Nanakos
        finally:
1274 e851ad95 Chrysostomos Nanakos
            self.lock_container_path = False
1275 e851ad95 Chrysostomos Nanakos
        dest_version_id = self._update_object_hash(
1276 e851ad95 Chrysostomos Nanakos
            user, account, container, name, size, type, mapfile, checksum,
1277 876d7486 Sofia Papagiannaki
            domain, meta, replace_meta, permissions, available=False)
1278 e851ad95 Chrysostomos Nanakos
        return self.node.version_get_properties(dest_version_id,
1279 e851ad95 Chrysostomos Nanakos
                                                keys=('uuid',))[0]
1280 e851ad95 Chrysostomos Nanakos
1281 e851ad95 Chrysostomos Nanakos
    @debug_method
1282 29148653 Sofia Papagiannaki
    def update_object_hashmap(self, user, account, container, name, size, type,
1283 29148653 Sofia Papagiannaki
                              hashmap, checksum, domain, meta=None,
1284 29148653 Sofia Papagiannaki
                              replace_meta=False, permissions=None):
1285 29148653 Sofia Papagiannaki
        """Create/update an object's hashmap and return the new version."""
1286 2715ade4 Sofia Papagiannaki
1287 1e47e49d Chrysostomos Nanakos
        for h in hashmap:
1288 1e47e49d Chrysostomos Nanakos
            if h.startswith('archip_'):
1289 1e47e49d Chrysostomos Nanakos
                raise IllegalOperationError(
1290 34f3d4fa Chrysostomos Nanakos
                    'Cannot update Archipelago Volume hashmap.')
1291 78348987 Sofia Papagiannaki
        meta = meta or {}
1292 2715ade4 Sofia Papagiannaki
        if size == 0:  # No such thing as an empty hashmap.
1293 6d64339e Antony Chazapis
            hashmap = [self.put_block('')]
1294 1c2fc0ff Antony Chazapis
        map = HashMap(self.block_size, self.hash_algorithm)
1295 3a5994a8 Sofia Papagiannaki
        map.extend([self._unhexlify_hash(x) for x in hashmap])
1296 7ca7bb08 Antony Chazapis
        missing = self.store.block_search(map)
1297 a9b3f29d Antony Chazapis
        if missing:
1298 a9b3f29d Antony Chazapis
            ie = IndexError()
1299 dd71f493 Antony Chazapis
            ie.data = [binascii.hexlify(x) for x in missing]
1300 a9b3f29d Antony Chazapis
            raise ie
1301 2715ade4 Sofia Papagiannaki
1302 1c2fc0ff Antony Chazapis
        hash = map.hash()
1303 133e3fcf Sofia Papagiannaki
        hexlified = binascii.hexlify(hash)
1304 683d4324 Sofia Papagiannaki
        # _update_object_hash() locks destination path
1305 29148653 Sofia Papagiannaki
        dest_version_id = self._update_object_hash(
1306 29148653 Sofia Papagiannaki
            user, account, container, name, size, type, hexlified, checksum,
1307 29148653 Sofia Papagiannaki
            domain, meta, replace_meta, permissions)
1308 7ca7bb08 Antony Chazapis
        self.store.map_put(hash, map)
1309 133e3fcf Sofia Papagiannaki
        return dest_version_id, hexlified
1310 2715ade4 Sofia Papagiannaki
1311 1f96b68d Sofia Papagiannaki
    @debug_method
1312 5d022141 Sofia Papagiannaki
    @backend_method
1313 29148653 Sofia Papagiannaki
    def update_object_checksum(self, user, account, container, name, version,
1314 29148653 Sofia Papagiannaki
                               checksum):
1315 33b4e4a6 Antony Chazapis
        """Update an object's checksum."""
1316 2715ade4 Sofia Papagiannaki
1317 29148653 Sofia Papagiannaki
        # Update objects with greater version and same hashmap
1318 29148653 Sofia Papagiannaki
        # and size (fix metadata updates).
1319 ebdbac7a Sofia Papagiannaki
        self._can_write_object(user, account, container, name)
1320 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1321 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
1322 33b4e4a6 Antony Chazapis
        props = self._get_version(node, version)
1323 33b4e4a6 Antony Chazapis
        versions = self.node.node_get_versions(node)
1324 33b4e4a6 Antony Chazapis
        for x in versions:
1325 29148653 Sofia Papagiannaki
            if (x[self.SERIAL] >= int(version) and
1326 29148653 Sofia Papagiannaki
                x[self.HASH] == props[self.HASH] and
1327 29148653 Sofia Papagiannaki
                    x[self.SIZE] == props[self.SIZE]):
1328 2715ade4 Sofia Papagiannaki
                self.node.version_put_property(
1329 2715ade4 Sofia Papagiannaki
                    x[self.SERIAL], 'checksum', checksum)
1330 2715ade4 Sofia Papagiannaki
1331 29148653 Sofia Papagiannaki
    def _copy_object(self, user, src_account, src_container, src_name,
1332 29148653 Sofia Papagiannaki
                     dest_account, dest_container, dest_name, type,
1333 29148653 Sofia Papagiannaki
                     dest_domain=None, dest_meta=None, replace_meta=False,
1334 29148653 Sofia Papagiannaki
                     permissions=None, src_version=None, is_move=False,
1335 29148653 Sofia Papagiannaki
                     delimiter=None):
1336 9e3a38bb Sofia Papagiannaki
1337 9e3a38bb Sofia Papagiannaki
        report_size_change = not is_move
1338 78348987 Sofia Papagiannaki
        dest_meta = dest_meta or {}
1339 4d15c94e Sofia Papagiannaki
        dest_version_ids = []
1340 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, src_account, src_container, src_name)
1341 a06c276e Sofia Papagiannaki
1342 a06c276e Sofia Papagiannaki
        src_container_path = '/'.join((src_account, src_container))
1343 a06c276e Sofia Papagiannaki
        dest_container_path = '/'.join((dest_account, dest_container))
1344 a06c276e Sofia Papagiannaki
        # Lock container paths in alphabetical order
1345 a06c276e Sofia Papagiannaki
        if src_container_path < dest_container_path:
1346 a06c276e Sofia Papagiannaki
            self._lookup_container(src_account, src_container)
1347 a06c276e Sofia Papagiannaki
            self._lookup_container(dest_account, dest_container)
1348 a06c276e Sofia Papagiannaki
        else:
1349 a06c276e Sofia Papagiannaki
            self._lookup_container(dest_account, dest_container)
1350 a06c276e Sofia Papagiannaki
            self._lookup_container(src_account, src_container)
1351 a06c276e Sofia Papagiannaki
1352 a06c276e Sofia Papagiannaki
        path, node = self._lookup_object(src_account, src_container, src_name)
1353 1730b3bf chazapis
        # TODO: Will do another fetch of the properties in duplicate version...
1354 2715ade4 Sofia Papagiannaki
        props = self._get_version(
1355 2715ade4 Sofia Papagiannaki
            node, src_version)  # Check to see if source exists.
1356 b9064632 Antony Chazapis
        src_version_id = props[self.SERIAL]
1357 b9064632 Antony Chazapis
        hash = props[self.HASH]
1358 b9064632 Antony Chazapis
        size = props[self.SIZE]
1359 2715ade4 Sofia Papagiannaki
        is_copy = not is_move and (src_account, src_container, src_name) != (
1360 2715ade4 Sofia Papagiannaki
            dest_account, dest_container, dest_name)  # New uuid.
1361 29148653 Sofia Papagiannaki
        dest_version_ids.append(self._update_object_hash(
1362 29148653 Sofia Papagiannaki
            user, dest_account, dest_container, dest_name, size, type, hash,
1363 29148653 Sofia Papagiannaki
            None, dest_domain, dest_meta, replace_meta, permissions,
1364 9e3a38bb Sofia Papagiannaki
            src_node=node, src_version_id=src_version_id, is_copy=is_copy,
1365 9e3a38bb Sofia Papagiannaki
            report_size_change=report_size_change))
1366 29148653 Sofia Papagiannaki
        if is_move and ((src_account, src_container, src_name) !=
1367 29148653 Sofia Papagiannaki
                        (dest_account, dest_container, dest_name)):
1368 9e3a38bb Sofia Papagiannaki
            self._delete_object(user, src_account, src_container, src_name,
1369 9e3a38bb Sofia Papagiannaki
                                report_size_change=report_size_change)
1370 2715ade4 Sofia Papagiannaki
1371 4d15c94e Sofia Papagiannaki
        if delimiter:
1372 29148653 Sofia Papagiannaki
            prefix = (src_name + delimiter if not
1373 29148653 Sofia Papagiannaki
                      src_name.endswith(delimiter) else src_name)
1374 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
1375 29148653 Sofia Papagiannaki
                user, src_account, src_container, prefix, delimiter=None,
1376 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
1377 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
1378 2715ade4 Sofia Papagiannaki
            src_names.sort(key=lambda x: x[2])  # order by nodes
1379 4d15c94e Sofia Papagiannaki
            paths = [elem[0] for elem in src_names]
1380 4d15c94e Sofia Papagiannaki
            nodes = [elem[2] for elem in src_names]
1381 29148653 Sofia Papagiannaki
            # TODO: Will do another fetch of the properties
1382 29148653 Sofia Papagiannaki
            # in duplicate version...
1383 2715ade4 Sofia Papagiannaki
            props = self._get_versions(nodes)  # Check to see if source exists.
1384 2715ade4 Sofia Papagiannaki
1385 4d15c94e Sofia Papagiannaki
            for prop, path, node in zip(props, paths, nodes):
1386 4d15c94e Sofia Papagiannaki
                src_version_id = prop[self.SERIAL]
1387 4d15c94e Sofia Papagiannaki
                hash = prop[self.HASH]
1388 4d15c94e Sofia Papagiannaki
                vtype = prop[self.TYPE]
1389 07867f70 Sofia Papagiannaki
                size = prop[self.SIZE]
1390 2715ade4 Sofia Papagiannaki
                dest_prefix = dest_name + delimiter if not dest_name.endswith(
1391 2715ade4 Sofia Papagiannaki
                    delimiter) else dest_name
1392 4d15c94e Sofia Papagiannaki
                vdest_name = path.replace(prefix, dest_prefix, 1)
1393 683d4324 Sofia Papagiannaki
                # _update_object_hash() locks destination path
1394 29148653 Sofia Papagiannaki
                dest_version_ids.append(self._update_object_hash(
1395 29148653 Sofia Papagiannaki
                    user, dest_account, dest_container, vdest_name, size,
1396 29148653 Sofia Papagiannaki
                    vtype, hash, None, dest_domain, meta={},
1397 29148653 Sofia Papagiannaki
                    replace_meta=False, permissions=None, src_node=node,
1398 277faddf Sofia Papagiannaki
                    src_version_id=src_version_id, is_copy=is_copy,
1399 277faddf Sofia Papagiannaki
                    report_size_change=report_size_change))
1400 29148653 Sofia Papagiannaki
                if is_move and ((src_account, src_container, src_name) !=
1401 29148653 Sofia Papagiannaki
                                (dest_account, dest_container, dest_name)):
1402 277faddf Sofia Papagiannaki
                    self._delete_object(user, src_account, src_container, path,
1403 277faddf Sofia Papagiannaki
                                        report_size_change=report_size_change)
1404 29148653 Sofia Papagiannaki
        return (dest_version_ids[0] if len(dest_version_ids) == 1 else
1405 29148653 Sofia Papagiannaki
                dest_version_ids)
1406 2715ade4 Sofia Papagiannaki
1407 1f96b68d Sofia Papagiannaki
    @debug_method
1408 5d022141 Sofia Papagiannaki
    @backend_method
1409 29148653 Sofia Papagiannaki
    def copy_object(self, user, src_account, src_container, src_name,
1410 29148653 Sofia Papagiannaki
                    dest_account, dest_container, dest_name, type, domain,
1411 29148653 Sofia Papagiannaki
                    meta=None, replace_meta=False, permissions=None,
1412 29148653 Sofia Papagiannaki
                    src_version=None, delimiter=None):
1413 dff7b6f1 Sofia Papagiannaki
        """Copy an object's data and metadata."""
1414 2715ade4 Sofia Papagiannaki
1415 78348987 Sofia Papagiannaki
        meta = meta or {}
1416 29148653 Sofia Papagiannaki
        dest_version_id = self._copy_object(
1417 29148653 Sofia Papagiannaki
            user, src_account, src_container, src_name, dest_account,
1418 29148653 Sofia Papagiannaki
            dest_container, dest_name, type, domain, meta, replace_meta,
1419 29148653 Sofia Papagiannaki
            permissions, src_version, False, delimiter)
1420 46286f5f Antony Chazapis
        return dest_version_id
1421 2715ade4 Sofia Papagiannaki
1422 1f96b68d Sofia Papagiannaki
    @debug_method
1423 5d022141 Sofia Papagiannaki
    @backend_method
1424 29148653 Sofia Papagiannaki
    def move_object(self, user, src_account, src_container, src_name,
1425 29148653 Sofia Papagiannaki
                    dest_account, dest_container, dest_name, type, domain,
1426 29148653 Sofia Papagiannaki
                    meta=None, replace_meta=False, permissions=None,
1427 29148653 Sofia Papagiannaki
                    delimiter=None):
1428 a9b3f29d Antony Chazapis
        """Move an object's data and metadata."""
1429 2715ade4 Sofia Papagiannaki
1430 78348987 Sofia Papagiannaki
        meta = meta or {}
1431 79bb41b7 Antony Chazapis
        if user != src_account:
1432 79bb41b7 Antony Chazapis
            raise NotAllowedError
1433 9e3a38bb Sofia Papagiannaki
        dest_version_id = self._move_object(
1434 29148653 Sofia Papagiannaki
            user, src_account, src_container, src_name, dest_account,
1435 29148653 Sofia Papagiannaki
            dest_container, dest_name, type, domain, meta, replace_meta,
1436 9e3a38bb Sofia Papagiannaki
            permissions, None, delimiter=delimiter)
1437 02c4d2ba Antony Chazapis
        return dest_version_id
1438 2715ade4 Sofia Papagiannaki
1439 29148653 Sofia Papagiannaki
    def _delete_object(self, user, account, container, name, until=None,
1440 9e3a38bb Sofia Papagiannaki
                       delimiter=None, report_size_change=True):
1441 a9b3f29d Antony Chazapis
        if user != account:
1442 a9b3f29d Antony Chazapis
            raise NotAllowedError
1443 2715ade4 Sofia Papagiannaki
1444 48bb9c89 Sofia Papagiannaki
        # lookup object and lock container path also
1445 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1446 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
1447 48bb9c89 Sofia Papagiannaki
1448 a9b3f29d Antony Chazapis
        if until is not None:
1449 c915d3bf Antony Chazapis
            if node is None:
1450 c915d3bf Antony Chazapis
                return
1451 813e42e5 Antony Chazapis
            hashes = []
1452 813e42e5 Antony Chazapis
            size = 0
1453 388ea25f Sofia Papagiannaki
            serials = []
1454 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_NORMAL,
1455 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1456 813e42e5 Antony Chazapis
            hashes += h
1457 813e42e5 Antony Chazapis
            size += s
1458 388ea25f Sofia Papagiannaki
            serials += v
1459 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_HISTORY,
1460 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1461 813e42e5 Antony Chazapis
            hashes += h
1462 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
1463 0a92ff85 Sofia Papagiannaki
                size += s
1464 388ea25f Sofia Papagiannaki
            serials += v
1465 04230536 Antony Chazapis
            for h in hashes:
1466 04230536 Antony Chazapis
                self.store.map_delete(h)
1467 0f510652 Sofia Papagiannaki
            self.node.node_purge(node, until, CLUSTER_DELETED,
1468 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth=1)
1469 a9b3f29d Antony Chazapis
            try:
1470 29148653 Sofia Papagiannaki
                self._get_version(node)
1471 a9b3f29d Antony Chazapis
            except NameError:
1472 0f9d752c Antony Chazapis
                self.permissions.access_clear(path)
1473 0a92ff85 Sofia Papagiannaki
            self._report_size_change(
1474 0a92ff85 Sofia Papagiannaki
                user, account, -size, {
1475 0a92ff85 Sofia Papagiannaki
                    'action': 'object purge',
1476 0a92ff85 Sofia Papagiannaki
                    'path': path,
1477 0a92ff85 Sofia Papagiannaki
                    'versions': ','.join(str(i) for i in serials)
1478 0a92ff85 Sofia Papagiannaki
                }
1479 0a92ff85 Sofia Papagiannaki
            )
1480 a9b3f29d Antony Chazapis
            return
1481 2715ade4 Sofia Papagiannaki
1482 33af031c Sofia Papagiannaki
        if not self._exists(node):
1483 33af031c Sofia Papagiannaki
            raise ItemNotExists('Object is deleted.')
1484 ed2064f8 Christos Stavrakakis
1485 0f510652 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1486 0f510652 Sofia Papagiannaki
            user, node, size=0, type='', hash=None, checksum='',
1487 0f510652 Sofia Papagiannaki
            cluster=CLUSTER_DELETED, update_statistics_ancestors_depth=1)
1488 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, src_version_id,
1489 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
1490 9e3a38bb Sofia Papagiannaki
        if report_size_change:
1491 9e3a38bb Sofia Papagiannaki
            self._report_size_change(
1492 9e3a38bb Sofia Papagiannaki
                user, account, -del_size,
1493 9e3a38bb Sofia Papagiannaki
                {'action': 'object delete',
1494 9e3a38bb Sofia Papagiannaki
                 'path': path,
1495 9e3a38bb Sofia Papagiannaki
                 'versions': ','.join([str(dest_version_id)])})
1496 2715ade4 Sofia Papagiannaki
        self._report_object_change(
1497 2715ade4 Sofia Papagiannaki
            user, account, path, details={'action': 'object delete'})
1498 0f9d752c Antony Chazapis
        self.permissions.access_clear(path)
1499 2715ade4 Sofia Papagiannaki
1500 4d15c94e Sofia Papagiannaki
        if delimiter:
1501 4d15c94e Sofia Papagiannaki
            prefix = name + delimiter if not name.endswith(delimiter) else name
1502 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
1503 29148653 Sofia Papagiannaki
                user, account, container, prefix, delimiter=None,
1504 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
1505 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
1506 4d15c94e Sofia Papagiannaki
            paths = []
1507 4d15c94e Sofia Papagiannaki
            for t in src_names:
1508 2715ade4 Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
1509 2715ade4 Sofia Papagiannaki
                node = t[2]
1510 33af031c Sofia Papagiannaki
                if not self._exists(node):
1511 33af031c Sofia Papagiannaki
                    continue
1512 0f510652 Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(
1513 0f510652 Sofia Papagiannaki
                    user, node, size=0, type='', hash=None, checksum='',
1514 0f510652 Sofia Papagiannaki
                    cluster=CLUSTER_DELETED,
1515 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1516 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
1517 0f510652 Sofia Papagiannaki
                    account, container, src_version_id,
1518 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1519 9e3a38bb Sofia Papagiannaki
                if report_size_change:
1520 9e3a38bb Sofia Papagiannaki
                    self._report_size_change(
1521 9e3a38bb Sofia Papagiannaki
                        user, account, -del_size,
1522 9e3a38bb Sofia Papagiannaki
                        {'action': 'object delete',
1523 9e3a38bb Sofia Papagiannaki
                         'path': path,
1524 9e3a38bb Sofia Papagiannaki
                         'versions': ','.join([str(dest_version_id)])})
1525 2715ade4 Sofia Papagiannaki
                self._report_object_change(
1526 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
1527 4d15c94e Sofia Papagiannaki
                paths.append(path)
1528 4d15c94e Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
1529 2715ade4 Sofia Papagiannaki
1530 ebdbac7a Sofia Papagiannaki
        # remove all the cached allowed paths
1531 ebdbac7a Sofia Papagiannaki
        # removing the specific path could be more expensive
1532 ebdbac7a Sofia Papagiannaki
        self._reset_allowed_paths()
1533 ebdbac7a Sofia Papagiannaki
1534 1f96b68d Sofia Papagiannaki
    @debug_method
1535 5d022141 Sofia Papagiannaki
    @backend_method
1536 29148653 Sofia Papagiannaki
    def delete_object(self, user, account, container, name, until=None,
1537 29148653 Sofia Papagiannaki
                      prefix='', delimiter=None):
1538 dff7b6f1 Sofia Papagiannaki
        """Delete/purge an object."""
1539 2715ade4 Sofia Papagiannaki
1540 4d15c94e Sofia Papagiannaki
        self._delete_object(user, account, container, name, until, delimiter)
1541 2715ade4 Sofia Papagiannaki
1542 1f96b68d Sofia Papagiannaki
    @debug_method
1543 5d022141 Sofia Papagiannaki
    @backend_method
1544 62f915a1 Antony Chazapis
    def list_versions(self, user, account, container, name):
1545 29148653 Sofia Papagiannaki
        """Return a list of all object (version, version_timestamp) tuples."""
1546 2715ade4 Sofia Papagiannaki
1547 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1548 60b8a083 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1549 97d45f69 Antony Chazapis
        versions = self.node.node_get_versions(node)
1550 29148653 Sofia Papagiannaki
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if
1551 29148653 Sofia Papagiannaki
                x[self.CLUSTER] != CLUSTER_DELETED]
1552 2715ade4 Sofia Papagiannaki
1553 1f96b68d Sofia Papagiannaki
    @debug_method
1554 5d022141 Sofia Papagiannaki
    @backend_method
1555 91fc9266 Sofia Papagiannaki
    def get_uuid(self, user, uuid, check_permissions=True):
1556 37bee317 Antony Chazapis
        """Return the (account, container, name) for the UUID given."""
1557 2715ade4 Sofia Papagiannaki
1558 2bbf1544 Georgios D. Tsoukalas
        info = self.node.latest_uuid(uuid, CLUSTER_NORMAL)
1559 37bee317 Antony Chazapis
        if info is None:
1560 37bee317 Antony Chazapis
            raise NameError
1561 37bee317 Antony Chazapis
        path, serial = info
1562 37bee317 Antony Chazapis
        account, container, name = path.split('/', 2)
1563 91fc9266 Sofia Papagiannaki
        if check_permissions:
1564 ebdbac7a Sofia Papagiannaki
            self._can_read_object(user, account, container, name)
1565 37bee317 Antony Chazapis
        return (account, container, name)
1566 2715ade4 Sofia Papagiannaki
1567 1f96b68d Sofia Papagiannaki
    @debug_method
1568 5d022141 Sofia Papagiannaki
    @backend_method
1569 bb4eafc6 Antony Chazapis
    def get_public(self, user, public):
1570 bb4eafc6 Antony Chazapis
        """Return the (account, container, name) for the public id given."""
1571 2715ade4 Sofia Papagiannaki
1572 56f3c759 Sofia Papagiannaki
        path = self.permissions.public_path(public)
1573 37bee317 Antony Chazapis
        if path is None:
1574 37bee317 Antony Chazapis
            raise NameError
1575 bb4eafc6 Antony Chazapis
        account, container, name = path.split('/', 2)
1576 ebdbac7a Sofia Papagiannaki
        self._can_read_object(user, account, container, name)
1577 bb4eafc6 Antony Chazapis
        return (account, container, name)
1578 2715ade4 Sofia Papagiannaki
1579 a9b3f29d Antony Chazapis
    def get_block(self, hash):
1580 a9b3f29d Antony Chazapis
        """Return a block's data."""
1581 2715ade4 Sofia Papagiannaki
1582 b1aca3e6 Sofia Papagiannaki
        logger.debug("get_block: %s", hash)
1583 1e47e49d Chrysostomos Nanakos
        if hash.startswith('archip_'):
1584 1e47e49d Chrysostomos Nanakos
            block = self.store.block_get_archipelago(hash)
1585 1e47e49d Chrysostomos Nanakos
        else:
1586 1e47e49d Chrysostomos Nanakos
            block = self.store.block_get(self._unhexlify_hash(hash))
1587 7ca7bb08 Antony Chazapis
        if not block:
1588 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Block does not exist')
1589 7ca7bb08 Antony Chazapis
        return block
1590 2715ade4 Sofia Papagiannaki
1591 a9b3f29d Antony Chazapis
    def put_block(self, data):
1592 60b8a083 Antony Chazapis
        """Store a block and return the hash."""
1593 2715ade4 Sofia Papagiannaki
1594 a9b3f29d Antony Chazapis
        logger.debug("put_block: %s", len(data))
1595 7ca7bb08 Antony Chazapis
        return binascii.hexlify(self.store.block_put(data))
1596 2715ade4 Sofia Papagiannaki
1597 a9b3f29d Antony Chazapis
    def update_block(self, hash, data, offset=0):
1598 a9b3f29d Antony Chazapis
        """Update a known block and return the hash."""
1599 2715ade4 Sofia Papagiannaki
1600 a9b3f29d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
1601 1e47e49d Chrysostomos Nanakos
        if hash.startswith('archip_'):
1602 1e47e49d Chrysostomos Nanakos
            raise IllegalOperationError(
1603 34f3d4fa Chrysostomos Nanakos
                'Cannot update an Archipelago Volume block.')
1604 a9b3f29d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
1605 a9b3f29d Antony Chazapis
            return self.put_block(data)
1606 3a5994a8 Sofia Papagiannaki
        h = self.store.block_update(self._unhexlify_hash(hash), offset, data)
1607 a9b3f29d Antony Chazapis
        return binascii.hexlify(h)
1608 2715ade4 Sofia Papagiannaki
1609 44ad5860 Antony Chazapis
    # Path functions.
1610 2715ade4 Sofia Papagiannaki
1611 37bee317 Antony Chazapis
    def _generate_uuid(self):
1612 37bee317 Antony Chazapis
        return str(uuidlib.uuid4())
1613 2715ade4 Sofia Papagiannaki
1614 b9064632 Antony Chazapis
    def _put_object_node(self, path, parent, name):
1615 c915d3bf Antony Chazapis
        path = '/'.join((path, name))
1616 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1617 c915d3bf Antony Chazapis
        if node is None:
1618 2e8edd42 Sofia Papagiannaki
            node = self.node.node_create(parent, path)
1619 c915d3bf Antony Chazapis
        return path, node
1620 2715ade4 Sofia Papagiannaki
1621 0f510652 Sofia Papagiannaki
    def _put_path(self, user, parent, path,
1622 0f510652 Sofia Papagiannaki
                  update_statistics_ancestors_depth=None):
1623 62f915a1 Antony Chazapis
        node = self.node.node_create(parent, path)
1624 2715ade4 Sofia Papagiannaki
        self.node.version_create(node, None, 0, '', None, user,
1625 0f510652 Sofia Papagiannaki
                                 self._generate_uuid(), '', CLUSTER_NORMAL,
1626 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth)
1627 62f915a1 Antony Chazapis
        return node
1628 2715ade4 Sofia Papagiannaki
1629 44ad5860 Antony Chazapis
    def _lookup_account(self, account, create=True):
1630 af395b9c Sofia Papagiannaki
        node = self.node.node_lookup(account)
1631 44ad5860 Antony Chazapis
        if node is None and create:
1632 2715ade4 Sofia Papagiannaki
            node = self._put_path(
1633 0f510652 Sofia Papagiannaki
                account, self.ROOTNODE, account,
1634 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=-1)  # User is account.
1635 c915d3bf Antony Chazapis
        return account, node
1636 2715ade4 Sofia Papagiannaki
1637 44ad5860 Antony Chazapis
    def _lookup_container(self, account, container):
1638 b90584d0 Sofia Papagiannaki
        for_update = True if self.lock_container_path else False
1639 c915d3bf Antony Chazapis
        path = '/'.join((account, container))
1640 b90584d0 Sofia Papagiannaki
        node = self.node.node_lookup(path, for_update)
1641 44ad5860 Antony Chazapis
        if node is None:
1642 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Container does not exist')
1643 c915d3bf Antony Chazapis
        return path, node
1644 2715ade4 Sofia Papagiannaki
1645 48bb9c89 Sofia Papagiannaki
    def _lookup_object(self, account, container, name, lock_container=False):
1646 48bb9c89 Sofia Papagiannaki
        if lock_container:
1647 48bb9c89 Sofia Papagiannaki
            self._lookup_container(account, container)
1648 48bb9c89 Sofia Papagiannaki
1649 c915d3bf Antony Chazapis
        path = '/'.join((account, container, name))
1650 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1651 44ad5860 Antony Chazapis
        if node is None:
1652 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Object does not exist')
1653 c915d3bf Antony Chazapis
        return path, node
1654 2715ade4 Sofia Papagiannaki
1655 cf4a7a7b Sofia Papagiannaki
    def _lookup_objects(self, paths):
1656 7efc9f86 Sofia Papagiannaki
        nodes = self.node.node_lookup_bulk(paths)
1657 cf4a7a7b Sofia Papagiannaki
        return paths, nodes
1658 2715ade4 Sofia Papagiannaki
1659 44ad5860 Antony Chazapis
    def _get_properties(self, node, until=None):
1660 44ad5860 Antony Chazapis
        """Return properties until the timestamp given."""
1661 2715ade4 Sofia Papagiannaki
1662 44ad5860 Antony Chazapis
        before = until if until is not None else inf
1663 44ad5860 Antony Chazapis
        props = self.node.version_lookup(node, before, CLUSTER_NORMAL)
1664 44ad5860 Antony Chazapis
        if props is None and until is not None:
1665 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, before, CLUSTER_HISTORY)
1666 44ad5860 Antony Chazapis
        if props is None:
1667 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Path does not exist')
1668 44ad5860 Antony Chazapis
        return props
1669 2715ade4 Sofia Papagiannaki
1670 2e8edd42 Sofia Papagiannaki
    def _get_statistics(self, node, until=None, compute=False):
1671 29148653 Sofia Papagiannaki
        """Return (count, sum of size, timestamp) of everything under node."""
1672 2715ade4 Sofia Papagiannaki
1673 2e8edd42 Sofia Papagiannaki
        if until is not None:
1674 62f915a1 Antony Chazapis
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1675 2e8edd42 Sofia Papagiannaki
        elif compute:
1676 29148653 Sofia Papagiannaki
            stats = self.node.statistics_latest(node,
1677 29148653 Sofia Papagiannaki
                                                except_cluster=CLUSTER_DELETED)
1678 2e8edd42 Sofia Papagiannaki
        else:
1679 2e8edd42 Sofia Papagiannaki
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1680 62f915a1 Antony Chazapis
        if stats is None:
1681 62f915a1 Antony Chazapis
            stats = (0, 0, 0)
1682 62f915a1 Antony Chazapis
        return stats
1683 2715ade4 Sofia Papagiannaki
1684 44ad5860 Antony Chazapis
    def _get_version(self, node, version=None):
1685 44ad5860 Antony Chazapis
        if version is None:
1686 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1687 44ad5860 Antony Chazapis
            if props is None:
1688 7efc9f86 Sofia Papagiannaki
                raise ItemNotExists('Object does not exist')
1689 44ad5860 Antony Chazapis
        else:
1690 07afd277 Antony Chazapis
            try:
1691 07afd277 Antony Chazapis
                version = int(version)
1692 07afd277 Antony Chazapis
            except ValueError:
1693 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1694 d2fc71c9 Sofia Papagiannaki
            props = self.node.version_get_properties(version, node=node)
1695 2c5363a0 Antony Chazapis
            if props is None or props[self.CLUSTER] == CLUSTER_DELETED:
1696 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1697 44ad5860 Antony Chazapis
        return props
1698 4d15c94e Sofia Papagiannaki
1699 7efc9f86 Sofia Papagiannaki
    def _get_versions(self, nodes):
1700 7efc9f86 Sofia Papagiannaki
        return self.node.version_lookup_bulk(nodes, inf, CLUSTER_NORMAL)
1701 2715ade4 Sofia Papagiannaki
1702 0f510652 Sofia Papagiannaki
    def _put_version_duplicate(self, user, node, src_node=None, size=None,
1703 0f510652 Sofia Papagiannaki
                               type=None, hash=None, checksum=None,
1704 0f510652 Sofia Papagiannaki
                               cluster=CLUSTER_NORMAL, is_copy=False,
1705 876d7486 Sofia Papagiannaki
                               update_statistics_ancestors_depth=None,
1706 e0525d86 Sofia Papagiannaki
                               available=True, keep_available=True):
1707 b9064632 Antony Chazapis
        """Create a new version of the node."""
1708 2715ade4 Sofia Papagiannaki
1709 2715ade4 Sofia Papagiannaki
        props = self.node.version_lookup(
1710 2715ade4 Sofia Papagiannaki
            node if src_node is None else src_node, inf, CLUSTER_NORMAL)
1711 b9064632 Antony Chazapis
        if props is not None:
1712 b9064632 Antony Chazapis
            src_version_id = props[self.SERIAL]
1713 b9064632 Antony Chazapis
            src_hash = props[self.HASH]
1714 b9064632 Antony Chazapis
            src_size = props[self.SIZE]
1715 66ce2ca5 Antony Chazapis
            src_type = props[self.TYPE]
1716 33b4e4a6 Antony Chazapis
            src_checksum = props[self.CHECKSUM]
1717 e0525d86 Sofia Papagiannaki
            if keep_available:
1718 e0525d86 Sofia Papagiannaki
                src_available = props[self.AVAILABLE]
1719 e0525d86 Sofia Papagiannaki
                src_map_check_timestamp = props[self.MAP_CHECK_TIMESTAMP]
1720 e0525d86 Sofia Papagiannaki
            else:
1721 e0525d86 Sofia Papagiannaki
                src_available = available
1722 e0525d86 Sofia Papagiannaki
                src_map_check_timestamp = None
1723 44ad5860 Antony Chazapis
        else:
1724 b9064632 Antony Chazapis
            src_version_id = None
1725 b9064632 Antony Chazapis
            src_hash = None
1726 b9064632 Antony Chazapis
            src_size = 0
1727 66ce2ca5 Antony Chazapis
            src_type = ''
1728 33b4e4a6 Antony Chazapis
            src_checksum = ''
1729 e0525d86 Sofia Papagiannaki
            src_available = available
1730 e0525d86 Sofia Papagiannaki
            src_map_check_timestamp = None
1731 2715ade4 Sofia Papagiannaki
        if size is None:  # Set metadata.
1732 29148653 Sofia Papagiannaki
            hash = src_hash  # This way hash can be set to None
1733 29148653 Sofia Papagiannaki
                             # (account or container).
1734 b9064632 Antony Chazapis
            size = src_size
1735 66ce2ca5 Antony Chazapis
        if type is None:
1736 66ce2ca5 Antony Chazapis
            type = src_type
1737 33b4e4a6 Antony Chazapis
        if checksum is None:
1738 33b4e4a6 Antony Chazapis
            checksum = src_checksum
1739 2715ade4 Sofia Papagiannaki
        uuid = self._generate_uuid(
1740 2715ade4 Sofia Papagiannaki
        ) if (is_copy or src_version_id is None) else props[self.UUID]
1741 2715ade4 Sofia Papagiannaki
1742 1730b3bf chazapis
        if src_node is None:
1743 1730b3bf chazapis
            pre_version_id = src_version_id
1744 1730b3bf chazapis
        else:
1745 1730b3bf chazapis
            pre_version_id = None
1746 1730b3bf chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1747 1730b3bf chazapis
            if props is not None:
1748 1730b3bf chazapis
                pre_version_id = props[self.SERIAL]
1749 1730b3bf chazapis
        if pre_version_id is not None:
1750 0f510652 Sofia Papagiannaki
            self.node.version_recluster(pre_version_id, CLUSTER_HISTORY,
1751 0f510652 Sofia Papagiannaki
                                        update_statistics_ancestors_depth)
1752 2715ade4 Sofia Papagiannaki
1753 0f510652 Sofia Papagiannaki
        dest_version_id, mtime = self.node.version_create(
1754 0f510652 Sofia Papagiannaki
            node, hash, size, type, src_version_id, user, uuid, checksum,
1755 876d7486 Sofia Papagiannaki
            cluster, update_statistics_ancestors_depth,
1756 e0525d86 Sofia Papagiannaki
            available=src_available,
1757 e0525d86 Sofia Papagiannaki
            map_check_timestamp=src_map_check_timestamp)
1758 ad9ada51 Sofia Papagiannaki
1759 ad9ada51 Sofia Papagiannaki
        self.node.attribute_unset_is_latest(node, dest_version_id)
1760 ad9ada51 Sofia Papagiannaki
1761 1730b3bf chazapis
        return pre_version_id, dest_version_id
1762 2715ade4 Sofia Papagiannaki
1763 ad9ada51 Sofia Papagiannaki
    def _put_metadata_duplicate(self, src_version_id, dest_version_id, domain,
1764 ad9ada51 Sofia Papagiannaki
                                node, meta, replace=False):
1765 4819d34f Antony Chazapis
        if src_version_id is not None:
1766 4819d34f Antony Chazapis
            self.node.attribute_copy(src_version_id, dest_version_id)
1767 4819d34f Antony Chazapis
        if not replace:
1768 2715ade4 Sofia Papagiannaki
            self.node.attribute_del(dest_version_id, domain, (
1769 2715ade4 Sofia Papagiannaki
                k for k, v in meta.iteritems() if v == ''))
1770 ad9ada51 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, (
1771 2715ade4 Sofia Papagiannaki
                (k, v) for k, v in meta.iteritems() if v != ''))
1772 4819d34f Antony Chazapis
        else:
1773 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain)
1774 ad9ada51 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, ((
1775 2715ade4 Sofia Papagiannaki
                k, v) for k, v in meta.iteritems()))
1776 2715ade4 Sofia Papagiannaki
1777 0f510652 Sofia Papagiannaki
    def _put_metadata(self, user, node, domain, meta, replace=False,
1778 0f510652 Sofia Papagiannaki
                      update_statistics_ancestors_depth=None):
1779 44ad5860 Antony Chazapis
        """Create a new version and store metadata."""
1780 2715ade4 Sofia Papagiannaki
1781 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1782 0f510652 Sofia Papagiannaki
            user, node,
1783 29148653 Sofia Papagiannaki
            update_statistics_ancestors_depth=
1784 29148653 Sofia Papagiannaki
            update_statistics_ancestors_depth)
1785 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1786 ad9ada51 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace)
1787 5cc484e1 Antony Chazapis
        return src_version_id, dest_version_id
1788 2715ade4 Sofia Papagiannaki
1789 60b8a083 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
1790 60b8a083 Antony Chazapis
        start = 0
1791 60b8a083 Antony Chazapis
        if marker:
1792 60b8a083 Antony Chazapis
            try:
1793 60b8a083 Antony Chazapis
                start = listing.index(marker) + 1
1794 60b8a083 Antony Chazapis
            except ValueError:
1795 60b8a083 Antony Chazapis
                pass
1796 60b8a083 Antony Chazapis
        if not limit or limit > 10000:
1797 60b8a083 Antony Chazapis
            limit = 10000
1798 60b8a083 Antony Chazapis
        return start, limit
1799 2715ade4 Sofia Papagiannaki
1800 29148653 Sofia Papagiannaki
    def _list_object_properties(self, parent, path, prefix='', delimiter=None,
1801 29148653 Sofia Papagiannaki
                                marker=None, limit=10000, virtual=True,
1802 29148653 Sofia Papagiannaki
                                domain=None, keys=None, until=None,
1803 29148653 Sofia Papagiannaki
                                size_range=None, allowed=None,
1804 29148653 Sofia Papagiannaki
                                all_props=False):
1805 78348987 Sofia Papagiannaki
        keys = keys or []
1806 78348987 Sofia Papagiannaki
        allowed = allowed or []
1807 60b8a083 Antony Chazapis
        cont_prefix = path + '/'
1808 60b8a083 Antony Chazapis
        prefix = cont_prefix + prefix
1809 60b8a083 Antony Chazapis
        start = cont_prefix + marker if marker else None
1810 60b8a083 Antony Chazapis
        before = until if until is not None else inf
1811 4819d34f Antony Chazapis
        filterq = keys if domain else []
1812 7ff57991 Antony Chazapis
        sizeq = size_range
1813 2715ade4 Sofia Papagiannaki
1814 29148653 Sofia Papagiannaki
        objects, prefixes = self.node.latest_version_list(
1815 29148653 Sofia Papagiannaki
            parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED,
1816 29148653 Sofia Papagiannaki
            allowed, domain, filterq, sizeq, all_props)
1817 60b8a083 Antony Chazapis
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1818 43be9afd Sofia Papagiannaki
        objects.sort(key=lambda x: x[0])
1819 371d907a Antony Chazapis
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
1820 cf4a7a7b Sofia Papagiannaki
        return objects
1821 2715ade4 Sofia Papagiannaki
1822 813e42e5 Antony Chazapis
    # Reporting functions.
1823 2715ade4 Sofia Papagiannaki
1824 1f96b68d Sofia Papagiannaki
    @debug_method
1825 5d022141 Sofia Papagiannaki
    @backend_method
1826 78348987 Sofia Papagiannaki
    def _report_size_change(self, user, account, size, details=None):
1827 78348987 Sofia Papagiannaki
        details = details or {}
1828 78348987 Sofia Papagiannaki
1829 8ed3f04c Sofia Papagiannaki
        if size == 0:
1830 8ed3f04c Sofia Papagiannaki
            return
1831 8ed3f04c Sofia Papagiannaki
1832 813e42e5 Antony Chazapis
        account_node = self._lookup_account(account, True)[1]
1833 2e8edd42 Sofia Papagiannaki
        total = self._get_statistics(account_node, compute=True)[1]
1834 813e42e5 Antony Chazapis
        details.update({'user': user, 'total': total})
1835 29148653 Sofia Papagiannaki
        self.messages.append(
1836 29148653 Sofia Papagiannaki
            (QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',),
1837 29148653 Sofia Papagiannaki
             account, QUEUE_INSTANCE_ID, 'diskspace', float(size), details))
1838 7ed99da8 root
1839 c846fad1 Sofia Papagiannaki
        if not self.using_external_quotaholder:
1840 73fbe301 Sofia Papagiannaki
            return
1841 7f1f0464 Georgios D. Tsoukalas
1842 ccfd4e44 Sofia Papagiannaki
        try:
1843 b17e5550 Giorgos Korfiatis
            name = details['path'] if 'path' in details else ''
1844 16f2673e Sofia Papagiannaki
            serial = self.astakosclient.issue_one_commission(
1845 b17e5550 Giorgos Korfiatis
                holder=account,
1846 b17e5550 Giorgos Korfiatis
                source=DEFAULT_SOURCE,
1847 b17e5550 Giorgos Korfiatis
                provisions={'pithos.diskspace': size},
1848 29148653 Sofia Papagiannaki
                name=name)
1849 ccfd4e44 Sofia Papagiannaki
        except BaseException, e:
1850 ccfd4e44 Sofia Papagiannaki
            raise QuotaError(e)
1851 ccfd4e44 Sofia Papagiannaki
        else:
1852 ccfd4e44 Sofia Papagiannaki
            self.serials.append(serial)
1853 0307b47f Georgios D. Tsoukalas
1854 1f96b68d Sofia Papagiannaki
    @debug_method
1855 5d022141 Sofia Papagiannaki
    @backend_method
1856 78348987 Sofia Papagiannaki
    def _report_object_change(self, user, account, path, details=None):
1857 78348987 Sofia Papagiannaki
        details = details or {}
1858 b82d3277 Sofia Papagiannaki
        details.update({'user': user})
1859 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',),
1860 29148653 Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'object', path,
1861 29148653 Sofia Papagiannaki
                              details))
1862 2715ade4 Sofia Papagiannaki
1863 1f96b68d Sofia Papagiannaki
    @debug_method
1864 5d022141 Sofia Papagiannaki
    @backend_method
1865 78348987 Sofia Papagiannaki
    def _report_sharing_change(self, user, account, path, details=None):
1866 78348987 Sofia Papagiannaki
        details = details or {}
1867 a74ba506 Sofia Papagiannaki
        details.update({'user': user})
1868 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',),
1869 29148653 Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'sharing', path,
1870 29148653 Sofia Papagiannaki
                              details))
1871 2715ade4 Sofia Papagiannaki
1872 60b8a083 Antony Chazapis
    # Policy functions.
1873 2715ade4 Sofia Papagiannaki
1874 19ddd41b Sofia Papagiannaki
    def _check_policy(self, policy, is_account_policy=True):
1875 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1876 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1877 60b8a083 Antony Chazapis
        for k in policy.keys():
1878 60b8a083 Antony Chazapis
            if policy[k] == '':
1879 19ddd41b Sofia Papagiannaki
                policy[k] = default_policy.get(k)
1880 60b8a083 Antony Chazapis
        for k, v in policy.iteritems():
1881 60b8a083 Antony Chazapis
            if k == 'quota':
1882 2715ade4 Sofia Papagiannaki
                q = int(v)  # May raise ValueError.
1883 60b8a083 Antony Chazapis
                if q < 0:
1884 60b8a083 Antony Chazapis
                    raise ValueError
1885 60b8a083 Antony Chazapis
            elif k == 'versioning':
1886 5cc484e1 Antony Chazapis
                if v not in ['auto', 'none']:
1887 60b8a083 Antony Chazapis
                    raise ValueError
1888 60b8a083 Antony Chazapis
            else:
1889 60b8a083 Antony Chazapis
                raise ValueError
1890 2715ade4 Sofia Papagiannaki
1891 19ddd41b Sofia Papagiannaki
    def _put_policy(self, node, policy, replace, is_account_policy=True):
1892 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1893 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1894 b2832c6a Antony Chazapis
        if replace:
1895 19ddd41b Sofia Papagiannaki
            for k, v in default_policy.iteritems():
1896 b2832c6a Antony Chazapis
                if k not in policy:
1897 b2832c6a Antony Chazapis
                    policy[k] = v
1898 b2832c6a Antony Chazapis
        self.node.policy_set(node, policy)
1899 2715ade4 Sofia Papagiannaki
1900 19ddd41b Sofia Papagiannaki
    def _get_policy(self, node, is_account_policy=True):
1901 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1902 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1903 19ddd41b Sofia Papagiannaki
        policy = default_policy.copy()
1904 b9064632 Antony Chazapis
        policy.update(self.node.policy_get(node))
1905 b9064632 Antony Chazapis
        return policy
1906 2715ade4 Sofia Papagiannaki
1907 0f510652 Sofia Papagiannaki
    def _apply_versioning(self, account, container, version_id,
1908 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
1909 813e42e5 Antony Chazapis
        """Delete the provided version if such is the policy.
1910 813e42e5 Antony Chazapis
           Return size of object removed.
1911 813e42e5 Antony Chazapis
        """
1912 2715ade4 Sofia Papagiannaki
1913 5cc484e1 Antony Chazapis
        if version_id is None:
1914 813e42e5 Antony Chazapis
            return 0
1915 5cc484e1 Antony Chazapis
        path, node = self._lookup_container(account, container)
1916 19ddd41b Sofia Papagiannaki
        versioning = self._get_policy(
1917 19ddd41b Sofia Papagiannaki
            node, is_account_policy=False)['versioning']
1918 1dd34bdd Sofia Papagiannaki
        if versioning != 'auto':
1919 0f510652 Sofia Papagiannaki
            hash, size = self.node.version_remove(
1920 0f510652 Sofia Papagiannaki
                version_id, update_statistics_ancestors_depth)
1921 5161c672 Antony Chazapis
            self.store.map_delete(hash)
1922 813e42e5 Antony Chazapis
            return size
1923 1dd34bdd Sofia Papagiannaki
        elif self.free_versioning:
1924 fff37615 Sofia Papagiannaki
            return self.node.version_get_properties(
1925 fff37615 Sofia Papagiannaki
                version_id, keys=('size',))[0]
1926 813e42e5 Antony Chazapis
        return 0
1927 2715ade4 Sofia Papagiannaki
1928 a9b3f29d Antony Chazapis
    # Access control functions.
1929 2715ade4 Sofia Papagiannaki
1930 a9b3f29d Antony Chazapis
    def _check_groups(self, groups):
1931 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in groups')
1932 a9b3f29d Antony Chazapis
        pass
1933 2715ade4 Sofia Papagiannaki
1934 a9b3f29d Antony Chazapis
    def _check_permissions(self, path, permissions):
1935 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in permissions')
1936 5e068361 Antony Chazapis
        pass
1937 2715ade4 Sofia Papagiannaki
1938 dc88754b Nanakos Chrysostomos
    def _get_formatted_paths(self, paths):
1939 dc88754b Nanakos Chrysostomos
        formatted = []
1940 ace7592b Sofia Papagiannaki
        if len(paths) == 0:
1941 dc88754b Nanakos Chrysostomos
            return formatted
1942 dc88754b Nanakos Chrysostomos
        props = self.node.get_props(paths)
1943 dc88754b Nanakos Chrysostomos
        if props:
1944 dc88754b Nanakos Chrysostomos
            for prop in props:
1945 dc88754b Nanakos Chrysostomos
                if prop[1].split(';', 1)[0].strip() in (
1946 dc88754b Nanakos Chrysostomos
                        'application/directory', 'application/folder'):
1947 ace7592b Sofia Papagiannaki
                    formatted.append((prop[0].rstrip('/') + '/',
1948 ace7592b Sofia Papagiannaki
                                      self.MATCH_PREFIX))
1949 dc88754b Nanakos Chrysostomos
                formatted.append((prop[0], self.MATCH_EXACT))
1950 dc88754b Nanakos Chrysostomos
        return formatted
1951 dc88754b Nanakos Chrysostomos
1952 16b0ed4a Nanakos Chrysostomos
    def _get_permissions_path(self, account, container, name):
1953 16b0ed4a Nanakos Chrysostomos
        path = '/'.join((account, container, name))
1954 16b0ed4a Nanakos Chrysostomos
        permission_paths = self.permissions.access_inherit(path)
1955 16b0ed4a Nanakos Chrysostomos
        permission_paths.sort()
1956 16b0ed4a Nanakos Chrysostomos
        permission_paths.reverse()
1957 16b0ed4a Nanakos Chrysostomos
        for p in permission_paths:
1958 16b0ed4a Nanakos Chrysostomos
            if p == path:
1959 16b0ed4a Nanakos Chrysostomos
                return p
1960 16b0ed4a Nanakos Chrysostomos
            else:
1961 16b0ed4a Nanakos Chrysostomos
                if p.count('/') < 2:
1962 16b0ed4a Nanakos Chrysostomos
                    continue
1963 16b0ed4a Nanakos Chrysostomos
                node = self.node.node_lookup(p)
1964 16b0ed4a Nanakos Chrysostomos
                props = None
1965 16b0ed4a Nanakos Chrysostomos
                if node is not None:
1966 16b0ed4a Nanakos Chrysostomos
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1967 16b0ed4a Nanakos Chrysostomos
                if props is not None:
1968 ace7592b Sofia Papagiannaki
                    if props[self.TYPE].split(';', 1)[0].strip() in (
1969 ace7592b Sofia Papagiannaki
                            'application/directory', 'application/folder'):
1970 16b0ed4a Nanakos Chrysostomos
                        return p
1971 16b0ed4a Nanakos Chrysostomos
        return None
1972 16b0ed4a Nanakos Chrysostomos
1973 dc88754b Nanakos Chrysostomos
    def _get_permissions_path_bulk(self, account, container, names):
1974 dc88754b Nanakos Chrysostomos
        formatted_paths = []
1975 dc88754b Nanakos Chrysostomos
        for name in names:
1976 dc88754b Nanakos Chrysostomos
            path = '/'.join((account, container, name))
1977 dc88754b Nanakos Chrysostomos
            formatted_paths.append(path)
1978 ace7592b Sofia Papagiannaki
        permission_paths = self.permissions.access_inherit_bulk(
1979 ace7592b Sofia Papagiannaki
            formatted_paths)
1980 5e068361 Antony Chazapis
        permission_paths.sort()
1981 5e068361 Antony Chazapis
        permission_paths.reverse()
1982 dc88754b Nanakos Chrysostomos
        permission_paths_list = []
1983 dc88754b Nanakos Chrysostomos
        lookup_list = []
1984 5e068361 Antony Chazapis
        for p in permission_paths:
1985 dc88754b Nanakos Chrysostomos
            if p in formatted_paths:
1986 dc88754b Nanakos Chrysostomos
                permission_paths_list.append(p)
1987 5e068361 Antony Chazapis
            else:
1988 71dbc012 Antony Chazapis
                if p.count('/') < 2:
1989 71dbc012 Antony Chazapis
                    continue
1990 dc88754b Nanakos Chrysostomos
                lookup_list.append(p)
1991 dc88754b Nanakos Chrysostomos
1992 dc88754b Nanakos Chrysostomos
        if len(lookup_list) > 0:
1993 62e6d12e Nanakos Chrysostomos
            props = self.node.get_props(lookup_list)
1994 dc88754b Nanakos Chrysostomos
            if props:
1995 dc88754b Nanakos Chrysostomos
                for prop in props:
1996 dc88754b Nanakos Chrysostomos
                    if prop[1].split(';', 1)[0].strip() in (
1997 29148653 Sofia Papagiannaki
                            'application/directory', 'application/folder'):
1998 e161c24f Sofia Papagiannaki
                        permission_paths_list.append(prop[0])
1999 dc88754b Nanakos Chrysostomos
2000 dc88754b Nanakos Chrysostomos
        if len(permission_paths_list) > 0:
2001 dc88754b Nanakos Chrysostomos
            return permission_paths_list
2002 dc88754b Nanakos Chrysostomos
2003 5e068361 Antony Chazapis
        return None
2004 2715ade4 Sofia Papagiannaki
2005 ebdbac7a Sofia Papagiannaki
    def _reset_allowed_paths(self):
2006 ebdbac7a Sofia Papagiannaki
        self.read_allowed_paths = defaultdict(set)
2007 ebdbac7a Sofia Papagiannaki
        self.write_allowed_paths = defaultdict(set)
2008 ebdbac7a Sofia Papagiannaki
2009 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=0)
2010 ebdbac7a Sofia Papagiannaki
    def _can_read_account(self, user, account):
2011 ebdbac7a Sofia Papagiannaki
        if user != account:
2012 ebdbac7a Sofia Papagiannaki
            if account not in self._allowed_accounts(user):
2013 ebdbac7a Sofia Papagiannaki
                raise NotAllowedError
2014 ebdbac7a Sofia Papagiannaki
2015 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=1)
2016 ebdbac7a Sofia Papagiannaki
    def _can_write_account(self, user, account):
2017 ebdbac7a Sofia Papagiannaki
        if user != account:
2018 ebdbac7a Sofia Papagiannaki
            raise NotAllowedError
2019 ebdbac7a Sofia Papagiannaki
2020 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=0)
2021 ebdbac7a Sofia Papagiannaki
    def _can_read_container(self, user, account, container):
2022 ebdbac7a Sofia Papagiannaki
        if user != account:
2023 ebdbac7a Sofia Papagiannaki
            if container not in self._allowed_containers(user, account):
2024 ebdbac7a Sofia Papagiannaki
                raise NotAllowedError
2025 ebdbac7a Sofia Papagiannaki
2026 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=1)
2027 ebdbac7a Sofia Papagiannaki
    def _can_write_container(self, user, account, container):
2028 ebdbac7a Sofia Papagiannaki
        if user != account:
2029 ebdbac7a Sofia Papagiannaki
            raise NotAllowedError
2030 ebdbac7a Sofia Papagiannaki
2031 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=0)
2032 ebdbac7a Sofia Papagiannaki
    def _can_read_object(self, user, account, container, name):
2033 a9b3f29d Antony Chazapis
        if user == account:
2034 a9b3f29d Antony Chazapis
            return True
2035 a9b3f29d Antony Chazapis
        path = '/'.join((account, container, name))
2036 aeb2b64f Antony Chazapis
        if self.permissions.public_get(path) is not None:
2037 71dbc012 Antony Chazapis
            return True
2038 5e068361 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
2039 71dbc012 Antony Chazapis
        if not path:
2040 71dbc012 Antony Chazapis
            raise NotAllowedError
2041 29148653 Sofia Papagiannaki
        if (not self.permissions.access_check(path, self.READ, user) and not
2042 29148653 Sofia Papagiannaki
                self.permissions.access_check(path, self.WRITE, user)):
2043 a9b3f29d Antony Chazapis
            raise NotAllowedError
2044 2715ade4 Sofia Papagiannaki
2045 ebdbac7a Sofia Papagiannaki
    @check_allowed_paths(action=1)
2046 ebdbac7a Sofia Papagiannaki
    def _can_write_object(self, user, account, container, name):
2047 6f4bce7b Antony Chazapis
        if user == account:
2048 6f4bce7b Antony Chazapis
            return True
2049 6f4bce7b Antony Chazapis
        path = '/'.join((account, container, name))
2050 71dbc012 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
2051 71dbc012 Antony Chazapis
        if not path:
2052 71dbc012 Antony Chazapis
            raise NotAllowedError
2053 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.WRITE, user):
2054 a9b3f29d Antony Chazapis
            raise NotAllowedError
2055 2715ade4 Sofia Papagiannaki
2056 a9b3f29d Antony Chazapis
    def _allowed_accounts(self, user):
2057 a9b3f29d Antony Chazapis
        allow = set()
2058 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user):
2059 ebdbac7a Sofia Papagiannaki
            p = path.split('/', 1)[0]
2060 ebdbac7a Sofia Papagiannaki
            allow.add(p)
2061 ebdbac7a Sofia Papagiannaki
        self.read_allowed_paths[user] |= allow
2062 a9b3f29d Antony Chazapis
        return sorted(allow)
2063 2715ade4 Sofia Papagiannaki
2064 a9b3f29d Antony Chazapis
    def _allowed_containers(self, user, account):
2065 a9b3f29d Antony Chazapis
        allow = set()
2066 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user, account):
2067 ebdbac7a Sofia Papagiannaki
            p = path.split('/', 2)[1]
2068 ebdbac7a Sofia Papagiannaki
            allow.add(p)
2069 ebdbac7a Sofia Papagiannaki
        self.read_allowed_paths[user] |= allow
2070 a9b3f29d Antony Chazapis
        return sorted(allow)
2071 5576e6dd Sofia Papagiannaki
2072 5576e6dd Sofia Papagiannaki
    # Domain functions
2073 5576e6dd Sofia Papagiannaki
2074 1f96b68d Sofia Papagiannaki
    @debug_method
2075 5d022141 Sofia Papagiannaki
    @backend_method
2076 e77b7a99 Sofia Papagiannaki
    def get_domain_objects(self, domain, user=None):
2077 a63f36a2 Sofia Papagiannaki
        allowed_paths = self.permissions.access_list_paths(
2078 a63f36a2 Sofia Papagiannaki
            user, include_owned=user is not None, include_containers=False)
2079 7736e11a Sofia Papagiannaki
        if not allowed_paths:
2080 7736e11a Sofia Papagiannaki
            return []
2081 7736e11a Sofia Papagiannaki
        obj_list = self.node.domain_object_list(
2082 7736e11a Sofia Papagiannaki
            domain, allowed_paths, CLUSTER_NORMAL)
2083 5576e6dd Sofia Papagiannaki
        return [(path,
2084 5576e6dd Sofia Papagiannaki
                 self._build_metadata(props, user_defined_meta),
2085 7736e11a Sofia Papagiannaki
                 self.permissions.access_get(path)) for
2086 7736e11a Sofia Papagiannaki
                path, props, user_defined_meta in obj_list]
2087 5576e6dd Sofia Papagiannaki
2088 5576e6dd Sofia Papagiannaki
    # util functions
2089 5576e6dd Sofia Papagiannaki
2090 5576e6dd Sofia Papagiannaki
    def _build_metadata(self, props, user_defined=None,
2091 5576e6dd Sofia Papagiannaki
                        include_user_defined=True):
2092 5576e6dd Sofia Papagiannaki
        meta = {'bytes': props[self.SIZE],
2093 5576e6dd Sofia Papagiannaki
                'type': props[self.TYPE],
2094 5576e6dd Sofia Papagiannaki
                'hash': props[self.HASH],
2095 5576e6dd Sofia Papagiannaki
                'version': props[self.SERIAL],
2096 5576e6dd Sofia Papagiannaki
                'version_timestamp': props[self.MTIME],
2097 5576e6dd Sofia Papagiannaki
                'modified_by': props[self.MUSER],
2098 5576e6dd Sofia Papagiannaki
                'uuid': props[self.UUID],
2099 5576e6dd Sofia Papagiannaki
                'checksum': props[self.CHECKSUM]}
2100 29148653 Sofia Papagiannaki
        if include_user_defined and user_defined is not None:
2101 5576e6dd Sofia Papagiannaki
            meta.update(user_defined)
2102 5576e6dd Sofia Papagiannaki
        return meta
2103 5576e6dd Sofia Papagiannaki
2104 33af031c Sofia Papagiannaki
    def _exists(self, node):
2105 33af031c Sofia Papagiannaki
        try:
2106 33af031c Sofia Papagiannaki
            self._get_version(node)
2107 33af031c Sofia Papagiannaki
        except ItemNotExists:
2108 33af031c Sofia Papagiannaki
            return False
2109 33af031c Sofia Papagiannaki
        else:
2110 33af031c Sofia Papagiannaki
            return True
2111 3a5994a8 Sofia Papagiannaki
2112 3a5994a8 Sofia Papagiannaki
    def _unhexlify_hash(self, hash):
2113 3a5994a8 Sofia Papagiannaki
        try:
2114 3a5994a8 Sofia Papagiannaki
            return binascii.unhexlify(hash)
2115 3a5994a8 Sofia Papagiannaki
        except TypeError:
2116 3a5994a8 Sofia Papagiannaki
            raise InvalidHash(hash)