Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 16b0ed4a

History | View | Annotate | Download (73.1 kB)

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

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