Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 4a7b190f

History | View | Annotate | Download (72.6 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
3 a9b3f29d Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 a9b3f29d Antony Chazapis
# without modification, are permitted provided that the following
5 a9b3f29d Antony Chazapis
# conditions are met:
6 2715ade4 Sofia Papagiannaki
#
7 a9b3f29d Antony Chazapis
#   1. Redistributions of source code must retain the above
8 a9b3f29d Antony Chazapis
#      copyright notice, this list of conditions and the following
9 a9b3f29d Antony Chazapis
#      disclaimer.
10 2715ade4 Sofia Papagiannaki
#
11 7ff57991 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 7ff57991 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 a9b3f29d Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 a9b3f29d Antony Chazapis
#      provided with the distribution.
15 2715ade4 Sofia Papagiannaki
#
16 a9b3f29d Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a9b3f29d Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a9b3f29d Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a9b3f29d Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a9b3f29d Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a9b3f29d Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a9b3f29d Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a9b3f29d Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a9b3f29d Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a9b3f29d Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a9b3f29d Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a9b3f29d Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 2715ade4 Sofia Papagiannaki
#
29 a9b3f29d Antony Chazapis
# The views and conclusions contained in the software and
30 a9b3f29d Antony Chazapis
# documentation are those of the authors and should not be
31 a9b3f29d Antony Chazapis
# interpreted as representing official policies, either expressed
32 a9b3f29d Antony Chazapis
# or implied, of GRNET S.A.
33 a9b3f29d Antony Chazapis
34 2c5363a0 Antony Chazapis
import sys
35 37bee317 Antony Chazapis
import uuid as uuidlib
36 a9b3f29d Antony Chazapis
import logging
37 6e147ecc Antony Chazapis
import hashlib
38 a9b3f29d Antony Chazapis
import binascii
39 a9b3f29d Antony Chazapis
40 9e3a38bb Sofia Papagiannaki
from functools import wraps, partial
41 1f96b68d Sofia Papagiannaki
from traceback import format_exc
42 1f96b68d Sofia Papagiannaki
43 7ee27246 Georgios D. Tsoukalas
try:
44 7ee27246 Georgios D. Tsoukalas
    from astakosclient import AstakosClient
45 7ee27246 Georgios D. Tsoukalas
except ImportError:
46 7ee27246 Georgios D. Tsoukalas
    AstakosClient = None
47 0307b47f Georgios D. Tsoukalas
48 19ddd41b Sofia Papagiannaki
from base import (DEFAULT_ACCOUNT_QUOTA, DEFAULT_CONTAINER_QUOTA,
49 19ddd41b Sofia Papagiannaki
                  DEFAULT_CONTAINER_VERSIONING, NotAllowedError, QuotaError,
50 19ddd41b Sofia Papagiannaki
                  BaseBackend, AccountExists, ContainerExists, AccountNotEmpty,
51 3a5994a8 Sofia Papagiannaki
                  ContainerNotEmpty, ItemNotExists, VersionNotExists,
52 3a5994a8 Sofia Papagiannaki
                  InvalidHash)
53 a9b3f29d Antony Chazapis
54 7ee27246 Georgios D. Tsoukalas
55 7ee27246 Georgios D. Tsoukalas
class DisabledAstakosClient(object):
56 7ee27246 Georgios D. Tsoukalas
    def __init__(self, *args, **kwargs):
57 7ee27246 Georgios D. Tsoukalas
        self.args = args
58 7ee27246 Georgios D. Tsoukalas
        self.kwargs = kwargs
59 7ee27246 Georgios D. Tsoukalas
60 7ee27246 Georgios D. Tsoukalas
    def __getattr__(self, name):
61 7ee27246 Georgios D. Tsoukalas
        m = ("AstakosClient has been disabled, "
62 7ee27246 Georgios D. Tsoukalas
             "yet an attempt to access it was made")
63 7ee27246 Georgios D. Tsoukalas
        raise AssertionError(m)
64 7ee27246 Georgios D. Tsoukalas
65 7ee27246 Georgios D. Tsoukalas
66 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
67 2715ade4 Sofia Papagiannaki
68 6e147ecc Antony Chazapis
class HashMap(list):
69 6e147ecc Antony Chazapis
70 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
71 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
72 6e147ecc Antony Chazapis
        self.blocksize = blocksize
73 6e147ecc Antony Chazapis
        self.blockhash = blockhash
74 6e147ecc Antony Chazapis
75 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
76 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
77 6e147ecc Antony Chazapis
        h.update(v)
78 6e147ecc Antony Chazapis
        return h.digest()
79 6e147ecc Antony Chazapis
80 6e147ecc Antony Chazapis
    def hash(self):
81 6e147ecc Antony Chazapis
        if len(self) == 0:
82 6e147ecc Antony Chazapis
            return self._hash_raw('')
83 6e147ecc Antony Chazapis
        if len(self) == 1:
84 6e147ecc Antony Chazapis
            return self.__getitem__(0)
85 6e147ecc Antony Chazapis
86 6e147ecc Antony Chazapis
        h = list(self)
87 6e147ecc Antony Chazapis
        s = 2
88 6e147ecc Antony Chazapis
        while s < len(h):
89 6e147ecc Antony Chazapis
            s = s * 2
90 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
91 6e147ecc Antony Chazapis
        while len(h) > 1:
92 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
93 6e147ecc Antony Chazapis
        return h[0]
94 5a96180b Antony Chazapis
95 228de81b Antony Chazapis
# Default modules and settings.
96 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
97 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
98 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
99 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
100 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
101 369a7b41 Sofia Papagiannaki
DEFAULT_BLOCK_SIZE = 4 * 1024 * 1024  # 4MB
102 369a7b41 Sofia Papagiannaki
DEFAULT_HASH_ALGORITHM = 'sha256'
103 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
104 29148653 Sofia Papagiannaki
DEFAULT_BLOCK_PARAMS = {'mappool': None, 'blockpool': None}
105 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
106 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
107 4a105ce2 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_ALPHABET = ('0123456789'
108 4a105ce2 Sofia Papagiannaki
                               'abcdefghijklmnopqrstuvwxyz'
109 4a105ce2 Sofia Papagiannaki
                               'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
110 c77af544 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_SECURITY = 16
111 fa9cae7e Antony Chazapis
112 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
113 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
114 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
115 228de81b Antony Chazapis
116 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
117 44ad5860 Antony Chazapis
118 44ad5860 Antony Chazapis
inf = float('inf')
119 44ad5860 Antony Chazapis
120 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
121 bb4eafc6 Antony Chazapis
122 b17e5550 Giorgos Korfiatis
DEFAULT_SOURCE = 'system'
123 a9b3f29d Antony Chazapis
124 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
125 a9b3f29d Antony Chazapis
126 1c2fc0ff Antony Chazapis
127 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 683d4324 Sofia Papagiannaki
        # _update_object_hash() locks destination path
1064 29148653 Sofia Papagiannaki
        dest_version_id = self._update_object_hash(
1065 29148653 Sofia Papagiannaki
            user, account, container, name, size, type, hexlified, checksum,
1066 29148653 Sofia Papagiannaki
            domain, meta, replace_meta, permissions)
1067 7ca7bb08 Antony Chazapis
        self.store.map_put(hash, map)
1068 133e3fcf Sofia Papagiannaki
        return dest_version_id, hexlified
1069 2715ade4 Sofia Papagiannaki
1070 1f96b68d Sofia Papagiannaki
    @debug_method
1071 29148653 Sofia Papagiannaki
    def update_object_checksum(self, user, account, container, name, version,
1072 29148653 Sofia Papagiannaki
                               checksum):
1073 33b4e4a6 Antony Chazapis
        """Update an object's checksum."""
1074 2715ade4 Sofia Papagiannaki
1075 29148653 Sofia Papagiannaki
        # Update objects with greater version and same hashmap
1076 29148653 Sofia Papagiannaki
        # and size (fix metadata updates).
1077 33b4e4a6 Antony Chazapis
        self._can_write(user, account, container, name)
1078 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1079 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
1080 33b4e4a6 Antony Chazapis
        props = self._get_version(node, version)
1081 33b4e4a6 Antony Chazapis
        versions = self.node.node_get_versions(node)
1082 33b4e4a6 Antony Chazapis
        for x in versions:
1083 29148653 Sofia Papagiannaki
            if (x[self.SERIAL] >= int(version) and
1084 29148653 Sofia Papagiannaki
                x[self.HASH] == props[self.HASH] and
1085 29148653 Sofia Papagiannaki
                    x[self.SIZE] == props[self.SIZE]):
1086 2715ade4 Sofia Papagiannaki
                self.node.version_put_property(
1087 2715ade4 Sofia Papagiannaki
                    x[self.SERIAL], 'checksum', checksum)
1088 2715ade4 Sofia Papagiannaki
1089 29148653 Sofia Papagiannaki
    def _copy_object(self, user, src_account, src_container, src_name,
1090 29148653 Sofia Papagiannaki
                     dest_account, dest_container, dest_name, type,
1091 29148653 Sofia Papagiannaki
                     dest_domain=None, dest_meta=None, replace_meta=False,
1092 29148653 Sofia Papagiannaki
                     permissions=None, src_version=None, is_move=False,
1093 29148653 Sofia Papagiannaki
                     delimiter=None):
1094 9e3a38bb Sofia Papagiannaki
1095 9e3a38bb Sofia Papagiannaki
        report_size_change = not is_move
1096 78348987 Sofia Papagiannaki
        dest_meta = dest_meta or {}
1097 4d15c94e Sofia Papagiannaki
        dest_version_ids = []
1098 79bb41b7 Antony Chazapis
        self._can_read(user, src_account, src_container, src_name)
1099 a06c276e Sofia Papagiannaki
1100 a06c276e Sofia Papagiannaki
        src_container_path = '/'.join((src_account, src_container))
1101 a06c276e Sofia Papagiannaki
        dest_container_path = '/'.join((dest_account, dest_container))
1102 a06c276e Sofia Papagiannaki
        # Lock container paths in alphabetical order
1103 a06c276e Sofia Papagiannaki
        if src_container_path < dest_container_path:
1104 a06c276e Sofia Papagiannaki
            self._lookup_container(src_account, src_container)
1105 a06c276e Sofia Papagiannaki
            self._lookup_container(dest_account, dest_container)
1106 a06c276e Sofia Papagiannaki
        else:
1107 a06c276e Sofia Papagiannaki
            self._lookup_container(dest_account, dest_container)
1108 a06c276e Sofia Papagiannaki
            self._lookup_container(src_account, src_container)
1109 a06c276e Sofia Papagiannaki
1110 a06c276e Sofia Papagiannaki
        path, node = self._lookup_object(src_account, src_container, src_name)
1111 1730b3bf chazapis
        # TODO: Will do another fetch of the properties in duplicate version...
1112 2715ade4 Sofia Papagiannaki
        props = self._get_version(
1113 2715ade4 Sofia Papagiannaki
            node, src_version)  # Check to see if source exists.
1114 b9064632 Antony Chazapis
        src_version_id = props[self.SERIAL]
1115 b9064632 Antony Chazapis
        hash = props[self.HASH]
1116 b9064632 Antony Chazapis
        size = props[self.SIZE]
1117 2715ade4 Sofia Papagiannaki
        is_copy = not is_move and (src_account, src_container, src_name) != (
1118 2715ade4 Sofia Papagiannaki
            dest_account, dest_container, dest_name)  # New uuid.
1119 29148653 Sofia Papagiannaki
        dest_version_ids.append(self._update_object_hash(
1120 29148653 Sofia Papagiannaki
            user, dest_account, dest_container, dest_name, size, type, hash,
1121 29148653 Sofia Papagiannaki
            None, dest_domain, dest_meta, replace_meta, permissions,
1122 9e3a38bb Sofia Papagiannaki
            src_node=node, src_version_id=src_version_id, is_copy=is_copy,
1123 9e3a38bb Sofia Papagiannaki
            report_size_change=report_size_change))
1124 29148653 Sofia Papagiannaki
        if is_move and ((src_account, src_container, src_name) !=
1125 29148653 Sofia Papagiannaki
                        (dest_account, dest_container, dest_name)):
1126 9e3a38bb Sofia Papagiannaki
            self._delete_object(user, src_account, src_container, src_name,
1127 9e3a38bb Sofia Papagiannaki
                                report_size_change=report_size_change)
1128 2715ade4 Sofia Papagiannaki
1129 4d15c94e Sofia Papagiannaki
        if delimiter:
1130 29148653 Sofia Papagiannaki
            prefix = (src_name + delimiter if not
1131 29148653 Sofia Papagiannaki
                      src_name.endswith(delimiter) else src_name)
1132 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
1133 29148653 Sofia Papagiannaki
                user, src_account, src_container, prefix, delimiter=None,
1134 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
1135 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
1136 2715ade4 Sofia Papagiannaki
            src_names.sort(key=lambda x: x[2])  # order by nodes
1137 4d15c94e Sofia Papagiannaki
            paths = [elem[0] for elem in src_names]
1138 4d15c94e Sofia Papagiannaki
            nodes = [elem[2] for elem in src_names]
1139 29148653 Sofia Papagiannaki
            # TODO: Will do another fetch of the properties
1140 29148653 Sofia Papagiannaki
            # in duplicate version...
1141 2715ade4 Sofia Papagiannaki
            props = self._get_versions(nodes)  # Check to see if source exists.
1142 2715ade4 Sofia Papagiannaki
1143 4d15c94e Sofia Papagiannaki
            for prop, path, node in zip(props, paths, nodes):
1144 4d15c94e Sofia Papagiannaki
                src_version_id = prop[self.SERIAL]
1145 4d15c94e Sofia Papagiannaki
                hash = prop[self.HASH]
1146 4d15c94e Sofia Papagiannaki
                vtype = prop[self.TYPE]
1147 07867f70 Sofia Papagiannaki
                size = prop[self.SIZE]
1148 2715ade4 Sofia Papagiannaki
                dest_prefix = dest_name + delimiter if not dest_name.endswith(
1149 2715ade4 Sofia Papagiannaki
                    delimiter) else dest_name
1150 4d15c94e Sofia Papagiannaki
                vdest_name = path.replace(prefix, dest_prefix, 1)
1151 683d4324 Sofia Papagiannaki
                # _update_object_hash() locks destination path
1152 29148653 Sofia Papagiannaki
                dest_version_ids.append(self._update_object_hash(
1153 29148653 Sofia Papagiannaki
                    user, dest_account, dest_container, vdest_name, size,
1154 29148653 Sofia Papagiannaki
                    vtype, hash, None, dest_domain, meta={},
1155 29148653 Sofia Papagiannaki
                    replace_meta=False, permissions=None, src_node=node,
1156 29148653 Sofia Papagiannaki
                    src_version_id=src_version_id, is_copy=is_copy))
1157 29148653 Sofia Papagiannaki
                if is_move and ((src_account, src_container, src_name) !=
1158 29148653 Sofia Papagiannaki
                                (dest_account, dest_container, dest_name)):
1159 2715ade4 Sofia Papagiannaki
                    self._delete_object(user, src_account, src_container, path)
1160 29148653 Sofia Papagiannaki
        return (dest_version_ids[0] if len(dest_version_ids) == 1 else
1161 29148653 Sofia Papagiannaki
                dest_version_ids)
1162 2715ade4 Sofia Papagiannaki
1163 1f96b68d Sofia Papagiannaki
    @debug_method
1164 29148653 Sofia Papagiannaki
    def copy_object(self, user, src_account, src_container, src_name,
1165 29148653 Sofia Papagiannaki
                    dest_account, dest_container, dest_name, type, domain,
1166 29148653 Sofia Papagiannaki
                    meta=None, replace_meta=False, permissions=None,
1167 29148653 Sofia Papagiannaki
                    src_version=None, delimiter=None):
1168 dff7b6f1 Sofia Papagiannaki
        """Copy an object's data and metadata."""
1169 2715ade4 Sofia Papagiannaki
1170 78348987 Sofia Papagiannaki
        meta = meta or {}
1171 29148653 Sofia Papagiannaki
        dest_version_id = self._copy_object(
1172 29148653 Sofia Papagiannaki
            user, src_account, src_container, src_name, dest_account,
1173 29148653 Sofia Papagiannaki
            dest_container, dest_name, type, domain, meta, replace_meta,
1174 29148653 Sofia Papagiannaki
            permissions, src_version, False, delimiter)
1175 46286f5f Antony Chazapis
        return dest_version_id
1176 2715ade4 Sofia Papagiannaki
1177 1f96b68d Sofia Papagiannaki
    @debug_method
1178 29148653 Sofia Papagiannaki
    def move_object(self, user, src_account, src_container, src_name,
1179 29148653 Sofia Papagiannaki
                    dest_account, dest_container, dest_name, type, domain,
1180 29148653 Sofia Papagiannaki
                    meta=None, replace_meta=False, permissions=None,
1181 29148653 Sofia Papagiannaki
                    delimiter=None):
1182 a9b3f29d Antony Chazapis
        """Move an object's data and metadata."""
1183 2715ade4 Sofia Papagiannaki
1184 78348987 Sofia Papagiannaki
        meta = meta or {}
1185 79bb41b7 Antony Chazapis
        if user != src_account:
1186 79bb41b7 Antony Chazapis
            raise NotAllowedError
1187 9e3a38bb Sofia Papagiannaki
        dest_version_id = self._move_object(
1188 29148653 Sofia Papagiannaki
            user, src_account, src_container, src_name, dest_account,
1189 29148653 Sofia Papagiannaki
            dest_container, dest_name, type, domain, meta, replace_meta,
1190 9e3a38bb Sofia Papagiannaki
            permissions, None, delimiter=delimiter)
1191 02c4d2ba Antony Chazapis
        return dest_version_id
1192 2715ade4 Sofia Papagiannaki
1193 29148653 Sofia Papagiannaki
    def _delete_object(self, user, account, container, name, until=None,
1194 9e3a38bb Sofia Papagiannaki
                       delimiter=None, report_size_change=True):
1195 a9b3f29d Antony Chazapis
        if user != account:
1196 a9b3f29d Antony Chazapis
            raise NotAllowedError
1197 2715ade4 Sofia Papagiannaki
1198 48bb9c89 Sofia Papagiannaki
        # lookup object and lock container path also
1199 48bb9c89 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1200 48bb9c89 Sofia Papagiannaki
                                         lock_container=True)
1201 48bb9c89 Sofia Papagiannaki
1202 a9b3f29d Antony Chazapis
        if until is not None:
1203 c915d3bf Antony Chazapis
            if node is None:
1204 c915d3bf Antony Chazapis
                return
1205 813e42e5 Antony Chazapis
            hashes = []
1206 813e42e5 Antony Chazapis
            size = 0
1207 388ea25f Sofia Papagiannaki
            serials = []
1208 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_NORMAL,
1209 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1210 813e42e5 Antony Chazapis
            hashes += h
1211 813e42e5 Antony Chazapis
            size += s
1212 388ea25f Sofia Papagiannaki
            serials += v
1213 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_HISTORY,
1214 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1215 813e42e5 Antony Chazapis
            hashes += h
1216 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
1217 0a92ff85 Sofia Papagiannaki
                size += s
1218 388ea25f Sofia Papagiannaki
            serials += v
1219 04230536 Antony Chazapis
            for h in hashes:
1220 04230536 Antony Chazapis
                self.store.map_delete(h)
1221 0f510652 Sofia Papagiannaki
            self.node.node_purge(node, until, CLUSTER_DELETED,
1222 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth=1)
1223 a9b3f29d Antony Chazapis
            try:
1224 29148653 Sofia Papagiannaki
                self._get_version(node)
1225 a9b3f29d Antony Chazapis
            except NameError:
1226 0f9d752c Antony Chazapis
                self.permissions.access_clear(path)
1227 0a92ff85 Sofia Papagiannaki
            self._report_size_change(
1228 0a92ff85 Sofia Papagiannaki
                user, account, -size, {
1229 0a92ff85 Sofia Papagiannaki
                    'action': 'object purge',
1230 0a92ff85 Sofia Papagiannaki
                    'path': path,
1231 0a92ff85 Sofia Papagiannaki
                    'versions': ','.join(str(i) for i in serials)
1232 0a92ff85 Sofia Papagiannaki
                }
1233 0a92ff85 Sofia Papagiannaki
            )
1234 a9b3f29d Antony Chazapis
            return
1235 2715ade4 Sofia Papagiannaki
1236 33af031c Sofia Papagiannaki
        if not self._exists(node):
1237 33af031c Sofia Papagiannaki
            raise ItemNotExists('Object is deleted.')
1238 ed2064f8 Christos Stavrakakis
1239 0f510652 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1240 0f510652 Sofia Papagiannaki
            user, node, size=0, type='', hash=None, checksum='',
1241 0f510652 Sofia Papagiannaki
            cluster=CLUSTER_DELETED, update_statistics_ancestors_depth=1)
1242 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, src_version_id,
1243 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
1244 9e3a38bb Sofia Papagiannaki
        if report_size_change:
1245 9e3a38bb Sofia Papagiannaki
            self._report_size_change(
1246 9e3a38bb Sofia Papagiannaki
                user, account, -del_size,
1247 9e3a38bb Sofia Papagiannaki
                {'action': 'object delete',
1248 9e3a38bb Sofia Papagiannaki
                 'path': path,
1249 9e3a38bb Sofia Papagiannaki
                 'versions': ','.join([str(dest_version_id)])})
1250 2715ade4 Sofia Papagiannaki
        self._report_object_change(
1251 2715ade4 Sofia Papagiannaki
            user, account, path, details={'action': 'object delete'})
1252 0f9d752c Antony Chazapis
        self.permissions.access_clear(path)
1253 2715ade4 Sofia Papagiannaki
1254 4d15c94e Sofia Papagiannaki
        if delimiter:
1255 4d15c94e Sofia Papagiannaki
            prefix = name + delimiter if not name.endswith(delimiter) else name
1256 29148653 Sofia Papagiannaki
            src_names = self._list_objects_no_limit(
1257 29148653 Sofia Papagiannaki
                user, account, container, prefix, delimiter=None,
1258 29148653 Sofia Papagiannaki
                virtual=False, domain=None, keys=[], shared=False, until=None,
1259 29148653 Sofia Papagiannaki
                size_range=None, all_props=True, public=False)
1260 4d15c94e Sofia Papagiannaki
            paths = []
1261 4d15c94e Sofia Papagiannaki
            for t in src_names:
1262 2715ade4 Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
1263 2715ade4 Sofia Papagiannaki
                node = t[2]
1264 33af031c Sofia Papagiannaki
                if not self._exists(node):
1265 33af031c Sofia Papagiannaki
                    continue
1266 0f510652 Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(
1267 0f510652 Sofia Papagiannaki
                    user, node, size=0, type='', hash=None, checksum='',
1268 0f510652 Sofia Papagiannaki
                    cluster=CLUSTER_DELETED,
1269 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1270 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
1271 0f510652 Sofia Papagiannaki
                    account, container, src_version_id,
1272 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1273 9e3a38bb Sofia Papagiannaki
                if report_size_change:
1274 9e3a38bb Sofia Papagiannaki
                    self._report_size_change(
1275 9e3a38bb Sofia Papagiannaki
                        user, account, -del_size,
1276 9e3a38bb Sofia Papagiannaki
                        {'action': 'object delete',
1277 9e3a38bb Sofia Papagiannaki
                         'path': path,
1278 9e3a38bb Sofia Papagiannaki
                         'versions': ','.join([str(dest_version_id)])})
1279 2715ade4 Sofia Papagiannaki
                self._report_object_change(
1280 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
1281 4d15c94e Sofia Papagiannaki
                paths.append(path)
1282 4d15c94e Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
1283 2715ade4 Sofia Papagiannaki
1284 1f96b68d Sofia Papagiannaki
    @debug_method
1285 29148653 Sofia Papagiannaki
    def delete_object(self, user, account, container, name, until=None,
1286 29148653 Sofia Papagiannaki
                      prefix='', delimiter=None):
1287 dff7b6f1 Sofia Papagiannaki
        """Delete/purge an object."""
1288 2715ade4 Sofia Papagiannaki
1289 4d15c94e Sofia Papagiannaki
        self._delete_object(user, account, container, name, until, delimiter)
1290 2715ade4 Sofia Papagiannaki
1291 1f96b68d Sofia Papagiannaki
    @debug_method
1292 62f915a1 Antony Chazapis
    def list_versions(self, user, account, container, name):
1293 29148653 Sofia Papagiannaki
        """Return a list of all object (version, version_timestamp) tuples."""
1294 2715ade4 Sofia Papagiannaki
1295 62f915a1 Antony Chazapis
        self._can_read(user, account, container, name)
1296 60b8a083 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1297 97d45f69 Antony Chazapis
        versions = self.node.node_get_versions(node)
1298 29148653 Sofia Papagiannaki
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if
1299 29148653 Sofia Papagiannaki
                x[self.CLUSTER] != CLUSTER_DELETED]
1300 2715ade4 Sofia Papagiannaki
1301 1f96b68d Sofia Papagiannaki
    @debug_method
1302 37bee317 Antony Chazapis
    def get_uuid(self, user, uuid):
1303 37bee317 Antony Chazapis
        """Return the (account, container, name) for the UUID given."""
1304 2715ade4 Sofia Papagiannaki
1305 2bbf1544 Georgios D. Tsoukalas
        info = self.node.latest_uuid(uuid, CLUSTER_NORMAL)
1306 37bee317 Antony Chazapis
        if info is None:
1307 37bee317 Antony Chazapis
            raise NameError
1308 37bee317 Antony Chazapis
        path, serial = info
1309 37bee317 Antony Chazapis
        account, container, name = path.split('/', 2)
1310 37bee317 Antony Chazapis
        self._can_read(user, account, container, name)
1311 37bee317 Antony Chazapis
        return (account, container, name)
1312 2715ade4 Sofia Papagiannaki
1313 1f96b68d Sofia Papagiannaki
    @debug_method
1314 bb4eafc6 Antony Chazapis
    def get_public(self, user, public):
1315 bb4eafc6 Antony Chazapis
        """Return the (account, container, name) for the public id given."""
1316 2715ade4 Sofia Papagiannaki
1317 56f3c759 Sofia Papagiannaki
        path = self.permissions.public_path(public)
1318 37bee317 Antony Chazapis
        if path is None:
1319 37bee317 Antony Chazapis
            raise NameError
1320 bb4eafc6 Antony Chazapis
        account, container, name = path.split('/', 2)
1321 bb4eafc6 Antony Chazapis
        self._can_read(user, account, container, name)
1322 bb4eafc6 Antony Chazapis
        return (account, container, name)
1323 2715ade4 Sofia Papagiannaki
1324 a9b3f29d Antony Chazapis
    def get_block(self, hash):
1325 a9b3f29d Antony Chazapis
        """Return a block's data."""
1326 2715ade4 Sofia Papagiannaki
1327 b1aca3e6 Sofia Papagiannaki
        logger.debug("get_block: %s", hash)
1328 3a5994a8 Sofia Papagiannaki
        block = self.store.block_get(self._unhexlify_hash(hash))
1329 7ca7bb08 Antony Chazapis
        if not block:
1330 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Block does not exist')
1331 7ca7bb08 Antony Chazapis
        return block
1332 2715ade4 Sofia Papagiannaki
1333 a9b3f29d Antony Chazapis
    def put_block(self, data):
1334 60b8a083 Antony Chazapis
        """Store a block and return the hash."""
1335 2715ade4 Sofia Papagiannaki
1336 a9b3f29d Antony Chazapis
        logger.debug("put_block: %s", len(data))
1337 7ca7bb08 Antony Chazapis
        return binascii.hexlify(self.store.block_put(data))
1338 2715ade4 Sofia Papagiannaki
1339 a9b3f29d Antony Chazapis
    def update_block(self, hash, data, offset=0):
1340 a9b3f29d Antony Chazapis
        """Update a known block and return the hash."""
1341 2715ade4 Sofia Papagiannaki
1342 a9b3f29d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
1343 a9b3f29d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
1344 a9b3f29d Antony Chazapis
            return self.put_block(data)
1345 3a5994a8 Sofia Papagiannaki
        h = self.store.block_update(self._unhexlify_hash(hash), offset, data)
1346 a9b3f29d Antony Chazapis
        return binascii.hexlify(h)
1347 2715ade4 Sofia Papagiannaki
1348 44ad5860 Antony Chazapis
    # Path functions.
1349 2715ade4 Sofia Papagiannaki
1350 37bee317 Antony Chazapis
    def _generate_uuid(self):
1351 37bee317 Antony Chazapis
        return str(uuidlib.uuid4())
1352 2715ade4 Sofia Papagiannaki
1353 b9064632 Antony Chazapis
    def _put_object_node(self, path, parent, name):
1354 c915d3bf Antony Chazapis
        path = '/'.join((path, name))
1355 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1356 c915d3bf Antony Chazapis
        if node is None:
1357 2e8edd42 Sofia Papagiannaki
            node = self.node.node_create(parent, path)
1358 c915d3bf Antony Chazapis
        return path, node
1359 2715ade4 Sofia Papagiannaki
1360 0f510652 Sofia Papagiannaki
    def _put_path(self, user, parent, path,
1361 0f510652 Sofia Papagiannaki
                  update_statistics_ancestors_depth=None):
1362 62f915a1 Antony Chazapis
        node = self.node.node_create(parent, path)
1363 2715ade4 Sofia Papagiannaki
        self.node.version_create(node, None, 0, '', None, user,
1364 0f510652 Sofia Papagiannaki
                                 self._generate_uuid(), '', CLUSTER_NORMAL,
1365 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth)
1366 62f915a1 Antony Chazapis
        return node
1367 2715ade4 Sofia Papagiannaki
1368 44ad5860 Antony Chazapis
    def _lookup_account(self, account, create=True):
1369 af395b9c Sofia Papagiannaki
        node = self.node.node_lookup(account)
1370 44ad5860 Antony Chazapis
        if node is None and create:
1371 2715ade4 Sofia Papagiannaki
            node = self._put_path(
1372 0f510652 Sofia Papagiannaki
                account, self.ROOTNODE, account,
1373 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=-1)  # User is account.
1374 c915d3bf Antony Chazapis
        return account, node
1375 2715ade4 Sofia Papagiannaki
1376 44ad5860 Antony Chazapis
    def _lookup_container(self, account, container):
1377 b90584d0 Sofia Papagiannaki
        for_update = True if self.lock_container_path else False
1378 c915d3bf Antony Chazapis
        path = '/'.join((account, container))
1379 b90584d0 Sofia Papagiannaki
        node = self.node.node_lookup(path, for_update)
1380 44ad5860 Antony Chazapis
        if node is None:
1381 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Container does not exist')
1382 c915d3bf Antony Chazapis
        return path, node
1383 2715ade4 Sofia Papagiannaki
1384 48bb9c89 Sofia Papagiannaki
    def _lookup_object(self, account, container, name, lock_container=False):
1385 48bb9c89 Sofia Papagiannaki
        if lock_container:
1386 48bb9c89 Sofia Papagiannaki
            self._lookup_container(account, container)
1387 48bb9c89 Sofia Papagiannaki
1388 c915d3bf Antony Chazapis
        path = '/'.join((account, container, name))
1389 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1390 44ad5860 Antony Chazapis
        if node is None:
1391 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Object does not exist')
1392 c915d3bf Antony Chazapis
        return path, node
1393 2715ade4 Sofia Papagiannaki
1394 cf4a7a7b Sofia Papagiannaki
    def _lookup_objects(self, paths):
1395 7efc9f86 Sofia Papagiannaki
        nodes = self.node.node_lookup_bulk(paths)
1396 cf4a7a7b Sofia Papagiannaki
        return paths, nodes
1397 2715ade4 Sofia Papagiannaki
1398 44ad5860 Antony Chazapis
    def _get_properties(self, node, until=None):
1399 44ad5860 Antony Chazapis
        """Return properties until the timestamp given."""
1400 2715ade4 Sofia Papagiannaki
1401 44ad5860 Antony Chazapis
        before = until if until is not None else inf
1402 44ad5860 Antony Chazapis
        props = self.node.version_lookup(node, before, CLUSTER_NORMAL)
1403 44ad5860 Antony Chazapis
        if props is None and until is not None:
1404 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, before, CLUSTER_HISTORY)
1405 44ad5860 Antony Chazapis
        if props is None:
1406 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Path does not exist')
1407 44ad5860 Antony Chazapis
        return props
1408 2715ade4 Sofia Papagiannaki
1409 2e8edd42 Sofia Papagiannaki
    def _get_statistics(self, node, until=None, compute=False):
1410 29148653 Sofia Papagiannaki
        """Return (count, sum of size, timestamp) of everything under node."""
1411 2715ade4 Sofia Papagiannaki
1412 2e8edd42 Sofia Papagiannaki
        if until is not None:
1413 62f915a1 Antony Chazapis
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1414 2e8edd42 Sofia Papagiannaki
        elif compute:
1415 29148653 Sofia Papagiannaki
            stats = self.node.statistics_latest(node,
1416 29148653 Sofia Papagiannaki
                                                except_cluster=CLUSTER_DELETED)
1417 2e8edd42 Sofia Papagiannaki
        else:
1418 2e8edd42 Sofia Papagiannaki
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1419 62f915a1 Antony Chazapis
        if stats is None:
1420 62f915a1 Antony Chazapis
            stats = (0, 0, 0)
1421 62f915a1 Antony Chazapis
        return stats
1422 2715ade4 Sofia Papagiannaki
1423 44ad5860 Antony Chazapis
    def _get_version(self, node, version=None):
1424 44ad5860 Antony Chazapis
        if version is None:
1425 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1426 44ad5860 Antony Chazapis
            if props is None:
1427 7efc9f86 Sofia Papagiannaki
                raise ItemNotExists('Object does not exist')
1428 44ad5860 Antony Chazapis
        else:
1429 07afd277 Antony Chazapis
            try:
1430 07afd277 Antony Chazapis
                version = int(version)
1431 07afd277 Antony Chazapis
            except ValueError:
1432 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1433 d2fc71c9 Sofia Papagiannaki
            props = self.node.version_get_properties(version, node=node)
1434 2c5363a0 Antony Chazapis
            if props is None or props[self.CLUSTER] == CLUSTER_DELETED:
1435 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1436 44ad5860 Antony Chazapis
        return props
1437 4d15c94e Sofia Papagiannaki
1438 7efc9f86 Sofia Papagiannaki
    def _get_versions(self, nodes):
1439 7efc9f86 Sofia Papagiannaki
        return self.node.version_lookup_bulk(nodes, inf, CLUSTER_NORMAL)
1440 2715ade4 Sofia Papagiannaki
1441 0f510652 Sofia Papagiannaki
    def _put_version_duplicate(self, user, node, src_node=None, size=None,
1442 0f510652 Sofia Papagiannaki
                               type=None, hash=None, checksum=None,
1443 0f510652 Sofia Papagiannaki
                               cluster=CLUSTER_NORMAL, is_copy=False,
1444 0f510652 Sofia Papagiannaki
                               update_statistics_ancestors_depth=None):
1445 b9064632 Antony Chazapis
        """Create a new version of the node."""
1446 2715ade4 Sofia Papagiannaki
1447 2715ade4 Sofia Papagiannaki
        props = self.node.version_lookup(
1448 2715ade4 Sofia Papagiannaki
            node if src_node is None else src_node, inf, CLUSTER_NORMAL)
1449 b9064632 Antony Chazapis
        if props is not None:
1450 b9064632 Antony Chazapis
            src_version_id = props[self.SERIAL]
1451 b9064632 Antony Chazapis
            src_hash = props[self.HASH]
1452 b9064632 Antony Chazapis
            src_size = props[self.SIZE]
1453 66ce2ca5 Antony Chazapis
            src_type = props[self.TYPE]
1454 33b4e4a6 Antony Chazapis
            src_checksum = props[self.CHECKSUM]
1455 44ad5860 Antony Chazapis
        else:
1456 b9064632 Antony Chazapis
            src_version_id = None
1457 b9064632 Antony Chazapis
            src_hash = None
1458 b9064632 Antony Chazapis
            src_size = 0
1459 66ce2ca5 Antony Chazapis
            src_type = ''
1460 33b4e4a6 Antony Chazapis
            src_checksum = ''
1461 2715ade4 Sofia Papagiannaki
        if size is None:  # Set metadata.
1462 29148653 Sofia Papagiannaki
            hash = src_hash  # This way hash can be set to None
1463 29148653 Sofia Papagiannaki
                             # (account or container).
1464 b9064632 Antony Chazapis
            size = src_size
1465 66ce2ca5 Antony Chazapis
        if type is None:
1466 66ce2ca5 Antony Chazapis
            type = src_type
1467 33b4e4a6 Antony Chazapis
        if checksum is None:
1468 33b4e4a6 Antony Chazapis
            checksum = src_checksum
1469 2715ade4 Sofia Papagiannaki
        uuid = self._generate_uuid(
1470 2715ade4 Sofia Papagiannaki
        ) if (is_copy or src_version_id is None) else props[self.UUID]
1471 2715ade4 Sofia Papagiannaki
1472 1730b3bf chazapis
        if src_node is None:
1473 1730b3bf chazapis
            pre_version_id = src_version_id
1474 1730b3bf chazapis
        else:
1475 1730b3bf chazapis
            pre_version_id = None
1476 1730b3bf chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1477 1730b3bf chazapis
            if props is not None:
1478 1730b3bf chazapis
                pre_version_id = props[self.SERIAL]
1479 1730b3bf chazapis
        if pre_version_id is not None:
1480 0f510652 Sofia Papagiannaki
            self.node.version_recluster(pre_version_id, CLUSTER_HISTORY,
1481 0f510652 Sofia Papagiannaki
                                        update_statistics_ancestors_depth)
1482 2715ade4 Sofia Papagiannaki
1483 0f510652 Sofia Papagiannaki
        dest_version_id, mtime = self.node.version_create(
1484 0f510652 Sofia Papagiannaki
            node, hash, size, type, src_version_id, user, uuid, checksum,
1485 0f510652 Sofia Papagiannaki
            cluster, update_statistics_ancestors_depth)
1486 ad9ada51 Sofia Papagiannaki
1487 ad9ada51 Sofia Papagiannaki
        self.node.attribute_unset_is_latest(node, dest_version_id)
1488 ad9ada51 Sofia Papagiannaki
1489 1730b3bf chazapis
        return pre_version_id, dest_version_id
1490 2715ade4 Sofia Papagiannaki
1491 ad9ada51 Sofia Papagiannaki
    def _put_metadata_duplicate(self, src_version_id, dest_version_id, domain,
1492 ad9ada51 Sofia Papagiannaki
                                node, meta, replace=False):
1493 4819d34f Antony Chazapis
        if src_version_id is not None:
1494 4819d34f Antony Chazapis
            self.node.attribute_copy(src_version_id, dest_version_id)
1495 4819d34f Antony Chazapis
        if not replace:
1496 2715ade4 Sofia Papagiannaki
            self.node.attribute_del(dest_version_id, domain, (
1497 2715ade4 Sofia Papagiannaki
                k for k, v in meta.iteritems() if v == ''))
1498 ad9ada51 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, (
1499 2715ade4 Sofia Papagiannaki
                (k, v) for k, v in meta.iteritems() if v != ''))
1500 4819d34f Antony Chazapis
        else:
1501 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain)
1502 ad9ada51 Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, ((
1503 2715ade4 Sofia Papagiannaki
                k, v) for k, v in meta.iteritems()))
1504 2715ade4 Sofia Papagiannaki
1505 0f510652 Sofia Papagiannaki
    def _put_metadata(self, user, node, domain, meta, replace=False,
1506 0f510652 Sofia Papagiannaki
                      update_statistics_ancestors_depth=None):
1507 44ad5860 Antony Chazapis
        """Create a new version and store metadata."""
1508 2715ade4 Sofia Papagiannaki
1509 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1510 0f510652 Sofia Papagiannaki
            user, node,
1511 29148653 Sofia Papagiannaki
            update_statistics_ancestors_depth=
1512 29148653 Sofia Papagiannaki
            update_statistics_ancestors_depth)
1513 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1514 ad9ada51 Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace)
1515 5cc484e1 Antony Chazapis
        return src_version_id, dest_version_id
1516 2715ade4 Sofia Papagiannaki
1517 60b8a083 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
1518 60b8a083 Antony Chazapis
        start = 0
1519 60b8a083 Antony Chazapis
        if marker:
1520 60b8a083 Antony Chazapis
            try:
1521 60b8a083 Antony Chazapis
                start = listing.index(marker) + 1
1522 60b8a083 Antony Chazapis
            except ValueError:
1523 60b8a083 Antony Chazapis
                pass
1524 60b8a083 Antony Chazapis
        if not limit or limit > 10000:
1525 60b8a083 Antony Chazapis
            limit = 10000
1526 60b8a083 Antony Chazapis
        return start, limit
1527 2715ade4 Sofia Papagiannaki
1528 29148653 Sofia Papagiannaki
    def _list_object_properties(self, parent, path, prefix='', delimiter=None,
1529 29148653 Sofia Papagiannaki
                                marker=None, limit=10000, virtual=True,
1530 29148653 Sofia Papagiannaki
                                domain=None, keys=None, until=None,
1531 29148653 Sofia Papagiannaki
                                size_range=None, allowed=None,
1532 29148653 Sofia Papagiannaki
                                all_props=False):
1533 78348987 Sofia Papagiannaki
        keys = keys or []
1534 78348987 Sofia Papagiannaki
        allowed = allowed or []
1535 60b8a083 Antony Chazapis
        cont_prefix = path + '/'
1536 60b8a083 Antony Chazapis
        prefix = cont_prefix + prefix
1537 60b8a083 Antony Chazapis
        start = cont_prefix + marker if marker else None
1538 60b8a083 Antony Chazapis
        before = until if until is not None else inf
1539 4819d34f Antony Chazapis
        filterq = keys if domain else []
1540 7ff57991 Antony Chazapis
        sizeq = size_range
1541 2715ade4 Sofia Papagiannaki
1542 29148653 Sofia Papagiannaki
        objects, prefixes = self.node.latest_version_list(
1543 29148653 Sofia Papagiannaki
            parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED,
1544 29148653 Sofia Papagiannaki
            allowed, domain, filterq, sizeq, all_props)
1545 60b8a083 Antony Chazapis
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1546 43be9afd Sofia Papagiannaki
        objects.sort(key=lambda x: x[0])
1547 371d907a Antony Chazapis
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
1548 cf4a7a7b Sofia Papagiannaki
        return objects
1549 2715ade4 Sofia Papagiannaki
1550 813e42e5 Antony Chazapis
    # Reporting functions.
1551 2715ade4 Sofia Papagiannaki
1552 1f96b68d Sofia Papagiannaki
    @debug_method
1553 78348987 Sofia Papagiannaki
    def _report_size_change(self, user, account, size, details=None):
1554 78348987 Sofia Papagiannaki
        details = details or {}
1555 78348987 Sofia Papagiannaki
1556 8ed3f04c Sofia Papagiannaki
        if size == 0:
1557 8ed3f04c Sofia Papagiannaki
            return
1558 8ed3f04c Sofia Papagiannaki
1559 813e42e5 Antony Chazapis
        account_node = self._lookup_account(account, True)[1]
1560 2e8edd42 Sofia Papagiannaki
        total = self._get_statistics(account_node, compute=True)[1]
1561 813e42e5 Antony Chazapis
        details.update({'user': user, 'total': total})
1562 29148653 Sofia Papagiannaki
        self.messages.append(
1563 29148653 Sofia Papagiannaki
            (QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',),
1564 29148653 Sofia Papagiannaki
             account, QUEUE_INSTANCE_ID, 'diskspace', float(size), details))
1565 7ed99da8 root
1566 c846fad1 Sofia Papagiannaki
        if not self.using_external_quotaholder:
1567 73fbe301 Sofia Papagiannaki
            return
1568 7f1f0464 Georgios D. Tsoukalas
1569 ccfd4e44 Sofia Papagiannaki
        try:
1570 b17e5550 Giorgos Korfiatis
            name = details['path'] if 'path' in details else ''
1571 16f2673e Sofia Papagiannaki
            serial = self.astakosclient.issue_one_commission(
1572 16f2673e Sofia Papagiannaki
                token=self.service_token,
1573 b17e5550 Giorgos Korfiatis
                holder=account,
1574 b17e5550 Giorgos Korfiatis
                source=DEFAULT_SOURCE,
1575 b17e5550 Giorgos Korfiatis
                provisions={'pithos.diskspace': size},
1576 29148653 Sofia Papagiannaki
                name=name)
1577 ccfd4e44 Sofia Papagiannaki
        except BaseException, e:
1578 ccfd4e44 Sofia Papagiannaki
            raise QuotaError(e)
1579 ccfd4e44 Sofia Papagiannaki
        else:
1580 ccfd4e44 Sofia Papagiannaki
            self.serials.append(serial)
1581 0307b47f Georgios D. Tsoukalas
1582 1f96b68d Sofia Papagiannaki
    @debug_method
1583 78348987 Sofia Papagiannaki
    def _report_object_change(self, user, account, path, details=None):
1584 78348987 Sofia Papagiannaki
        details = details or {}
1585 b82d3277 Sofia Papagiannaki
        details.update({'user': user})
1586 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',),
1587 29148653 Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'object', path,
1588 29148653 Sofia Papagiannaki
                              details))
1589 2715ade4 Sofia Papagiannaki
1590 1f96b68d Sofia Papagiannaki
    @debug_method
1591 78348987 Sofia Papagiannaki
    def _report_sharing_change(self, user, account, path, details=None):
1592 78348987 Sofia Papagiannaki
        details = details or {}
1593 a74ba506 Sofia Papagiannaki
        details.update({'user': user})
1594 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',),
1595 29148653 Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'sharing', path,
1596 29148653 Sofia Papagiannaki
                              details))
1597 2715ade4 Sofia Papagiannaki
1598 60b8a083 Antony Chazapis
    # Policy functions.
1599 2715ade4 Sofia Papagiannaki
1600 19ddd41b Sofia Papagiannaki
    def _check_policy(self, policy, is_account_policy=True):
1601 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1602 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1603 60b8a083 Antony Chazapis
        for k in policy.keys():
1604 60b8a083 Antony Chazapis
            if policy[k] == '':
1605 19ddd41b Sofia Papagiannaki
                policy[k] = default_policy.get(k)
1606 60b8a083 Antony Chazapis
        for k, v in policy.iteritems():
1607 60b8a083 Antony Chazapis
            if k == 'quota':
1608 2715ade4 Sofia Papagiannaki
                q = int(v)  # May raise ValueError.
1609 60b8a083 Antony Chazapis
                if q < 0:
1610 60b8a083 Antony Chazapis
                    raise ValueError
1611 60b8a083 Antony Chazapis
            elif k == 'versioning':
1612 5cc484e1 Antony Chazapis
                if v not in ['auto', 'none']:
1613 60b8a083 Antony Chazapis
                    raise ValueError
1614 60b8a083 Antony Chazapis
            else:
1615 60b8a083 Antony Chazapis
                raise ValueError
1616 2715ade4 Sofia Papagiannaki
1617 19ddd41b Sofia Papagiannaki
    def _put_policy(self, node, policy, replace, is_account_policy=True):
1618 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1619 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1620 b2832c6a Antony Chazapis
        if replace:
1621 19ddd41b Sofia Papagiannaki
            for k, v in default_policy.iteritems():
1622 b2832c6a Antony Chazapis
                if k not in policy:
1623 b2832c6a Antony Chazapis
                    policy[k] = v
1624 b2832c6a Antony Chazapis
        self.node.policy_set(node, policy)
1625 2715ade4 Sofia Papagiannaki
1626 19ddd41b Sofia Papagiannaki
    def _get_policy(self, node, is_account_policy=True):
1627 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1628 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1629 19ddd41b Sofia Papagiannaki
        policy = default_policy.copy()
1630 b9064632 Antony Chazapis
        policy.update(self.node.policy_get(node))
1631 b9064632 Antony Chazapis
        return policy
1632 2715ade4 Sofia Papagiannaki
1633 0f510652 Sofia Papagiannaki
    def _apply_versioning(self, account, container, version_id,
1634 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
1635 813e42e5 Antony Chazapis
        """Delete the provided version if such is the policy.
1636 813e42e5 Antony Chazapis
           Return size of object removed.
1637 813e42e5 Antony Chazapis
        """
1638 2715ade4 Sofia Papagiannaki
1639 5cc484e1 Antony Chazapis
        if version_id is None:
1640 813e42e5 Antony Chazapis
            return 0
1641 5cc484e1 Antony Chazapis
        path, node = self._lookup_container(account, container)
1642 19ddd41b Sofia Papagiannaki
        versioning = self._get_policy(
1643 19ddd41b Sofia Papagiannaki
            node, is_account_policy=False)['versioning']
1644 1dd34bdd Sofia Papagiannaki
        if versioning != 'auto':
1645 0f510652 Sofia Papagiannaki
            hash, size = self.node.version_remove(
1646 0f510652 Sofia Papagiannaki
                version_id, update_statistics_ancestors_depth)
1647 5161c672 Antony Chazapis
            self.store.map_delete(hash)
1648 813e42e5 Antony Chazapis
            return size
1649 1dd34bdd Sofia Papagiannaki
        elif self.free_versioning:
1650 fff37615 Sofia Papagiannaki
            return self.node.version_get_properties(
1651 fff37615 Sofia Papagiannaki
                version_id, keys=('size',))[0]
1652 813e42e5 Antony Chazapis
        return 0
1653 2715ade4 Sofia Papagiannaki
1654 a9b3f29d Antony Chazapis
    # Access control functions.
1655 2715ade4 Sofia Papagiannaki
1656 a9b3f29d Antony Chazapis
    def _check_groups(self, groups):
1657 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in groups')
1658 a9b3f29d Antony Chazapis
        pass
1659 2715ade4 Sofia Papagiannaki
1660 a9b3f29d Antony Chazapis
    def _check_permissions(self, path, permissions):
1661 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in permissions')
1662 5e068361 Antony Chazapis
        pass
1663 2715ade4 Sofia Papagiannaki
1664 2715ade4 Sofia Papagiannaki
1665 dc88754b Nanakos Chrysostomos
    def _get_formatted_paths(self, paths):
1666 dc88754b Nanakos Chrysostomos
        formatted = []
1667 dc88754b Nanakos Chrysostomos
        if len(paths) == 0 :
1668 dc88754b Nanakos Chrysostomos
            return formatted
1669 dc88754b Nanakos Chrysostomos
        props = self.node.get_props(paths)
1670 dc88754b Nanakos Chrysostomos
        if props:
1671 dc88754b Nanakos Chrysostomos
            for prop in props:
1672 dc88754b Nanakos Chrysostomos
                if prop[1].split(';', 1)[0].strip() in (
1673 dc88754b Nanakos Chrysostomos
                        'application/directory', 'application/folder'):
1674 dc88754b Nanakos Chrysostomos
                    formatted.append((prop[0].rstrip('/') + '/', self.MATCH_PREFIX))
1675 dc88754b Nanakos Chrysostomos
                formatted.append((prop[0], self.MATCH_EXACT))
1676 dc88754b Nanakos Chrysostomos
        return formatted
1677 dc88754b Nanakos Chrysostomos
1678 16b0ed4a Nanakos Chrysostomos
    def _get_permissions_path(self, account, container, name):
1679 16b0ed4a Nanakos Chrysostomos
        path = '/'.join((account, container, name))
1680 16b0ed4a Nanakos Chrysostomos
        permission_paths = self.permissions.access_inherit(path)
1681 16b0ed4a Nanakos Chrysostomos
        permission_paths.sort()
1682 16b0ed4a Nanakos Chrysostomos
        permission_paths.reverse()
1683 16b0ed4a Nanakos Chrysostomos
        for p in permission_paths:
1684 16b0ed4a Nanakos Chrysostomos
            if p == path:
1685 16b0ed4a Nanakos Chrysostomos
                return p
1686 16b0ed4a Nanakos Chrysostomos
            else:
1687 16b0ed4a Nanakos Chrysostomos
                if p.count('/') < 2:
1688 16b0ed4a Nanakos Chrysostomos
                    continue
1689 16b0ed4a Nanakos Chrysostomos
                node = self.node.node_lookup(p)
1690 16b0ed4a Nanakos Chrysostomos
                props = None
1691 16b0ed4a Nanakos Chrysostomos
                if node is not None:
1692 16b0ed4a Nanakos Chrysostomos
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1693 16b0ed4a Nanakos Chrysostomos
                if props is not None:
1694 16b0ed4a Nanakos Chrysostomos
                    if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1695 16b0ed4a Nanakos Chrysostomos
                        return p
1696 16b0ed4a Nanakos Chrysostomos
        return None
1697 16b0ed4a Nanakos Chrysostomos
1698 dc88754b Nanakos Chrysostomos
    def _get_permissions_path_bulk(self, account, container, names):
1699 dc88754b Nanakos Chrysostomos
        formatted_paths = []
1700 dc88754b Nanakos Chrysostomos
        for name in names:
1701 dc88754b Nanakos Chrysostomos
            path = '/'.join((account, container, name))
1702 dc88754b Nanakos Chrysostomos
            formatted_paths.append(path)
1703 dc88754b Nanakos Chrysostomos
        permission_paths = self.permissions.access_inherit_bulk(formatted_paths)
1704 5e068361 Antony Chazapis
        permission_paths.sort()
1705 5e068361 Antony Chazapis
        permission_paths.reverse()
1706 dc88754b Nanakos Chrysostomos
        permission_paths_list = []
1707 dc88754b Nanakos Chrysostomos
        lookup_list = []
1708 5e068361 Antony Chazapis
        for p in permission_paths:
1709 dc88754b Nanakos Chrysostomos
            if p in formatted_paths:
1710 dc88754b Nanakos Chrysostomos
                permission_paths_list.append(p)
1711 5e068361 Antony Chazapis
            else:
1712 71dbc012 Antony Chazapis
                if p.count('/') < 2:
1713 71dbc012 Antony Chazapis
                    continue
1714 dc88754b Nanakos Chrysostomos
                lookup_list.append(p)
1715 dc88754b Nanakos Chrysostomos
1716 dc88754b Nanakos Chrysostomos
        if len(lookup_list) > 0:
1717 dc88754b Nanakos Chrysostomos
            props = self.node.get_props(paths)
1718 dc88754b Nanakos Chrysostomos
            if props:
1719 dc88754b Nanakos Chrysostomos
                for prop in props:
1720 dc88754b Nanakos Chrysostomos
                    if prop[1].split(';', 1)[0].strip() in (
1721 29148653 Sofia Papagiannaki
                            'application/directory', 'application/folder'):
1722 dc88754b Nanakos Chrysostomos
                        permission_paths_list.append((prop[0].rstrip('/') + '/',self.MATCH_PREFIX))
1723 dc88754b Nanakos Chrysostomos
1724 dc88754b Nanakos Chrysostomos
        if len(permission_paths_list) > 0:
1725 dc88754b Nanakos Chrysostomos
            return permission_paths_list
1726 dc88754b Nanakos Chrysostomos
1727 5e068361 Antony Chazapis
        return None
1728 2715ade4 Sofia Papagiannaki
1729 6f4bce7b Antony Chazapis
    def _can_read(self, user, account, container, name):
1730 a9b3f29d Antony Chazapis
        if user == account:
1731 a9b3f29d Antony Chazapis
            return True
1732 a9b3f29d Antony Chazapis
        path = '/'.join((account, container, name))
1733 aeb2b64f Antony Chazapis
        if self.permissions.public_get(path) is not None:
1734 71dbc012 Antony Chazapis
            return True
1735 5e068361 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1736 71dbc012 Antony Chazapis
        if not path:
1737 71dbc012 Antony Chazapis
            raise NotAllowedError
1738 29148653 Sofia Papagiannaki
        if (not self.permissions.access_check(path, self.READ, user) and not
1739 29148653 Sofia Papagiannaki
                self.permissions.access_check(path, self.WRITE, user)):
1740 a9b3f29d Antony Chazapis
            raise NotAllowedError
1741 2715ade4 Sofia Papagiannaki
1742 a9b3f29d Antony Chazapis
    def _can_write(self, user, account, container, name):
1743 6f4bce7b Antony Chazapis
        if user == account:
1744 6f4bce7b Antony Chazapis
            return True
1745 6f4bce7b Antony Chazapis
        path = '/'.join((account, container, name))
1746 71dbc012 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1747 71dbc012 Antony Chazapis
        if not path:
1748 71dbc012 Antony Chazapis
            raise NotAllowedError
1749 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.WRITE, user):
1750 a9b3f29d Antony Chazapis
            raise NotAllowedError
1751 2715ade4 Sofia Papagiannaki
1752 a9b3f29d Antony Chazapis
    def _allowed_accounts(self, user):
1753 a9b3f29d Antony Chazapis
        allow = set()
1754 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user):
1755 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 1)[0])
1756 a9b3f29d Antony Chazapis
        return sorted(allow)
1757 2715ade4 Sofia Papagiannaki
1758 a9b3f29d Antony Chazapis
    def _allowed_containers(self, user, account):
1759 a9b3f29d Antony Chazapis
        allow = set()
1760 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user, account):
1761 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 2)[1])
1762 a9b3f29d Antony Chazapis
        return sorted(allow)
1763 5576e6dd Sofia Papagiannaki
1764 5576e6dd Sofia Papagiannaki
    # Domain functions
1765 5576e6dd Sofia Papagiannaki
1766 1f96b68d Sofia Papagiannaki
    @debug_method
1767 e77b7a99 Sofia Papagiannaki
    def get_domain_objects(self, domain, user=None):
1768 a63f36a2 Sofia Papagiannaki
        allowed_paths = self.permissions.access_list_paths(
1769 a63f36a2 Sofia Papagiannaki
            user, include_owned=user is not None, include_containers=False)
1770 7736e11a Sofia Papagiannaki
        if not allowed_paths:
1771 7736e11a Sofia Papagiannaki
            return []
1772 7736e11a Sofia Papagiannaki
        obj_list = self.node.domain_object_list(
1773 7736e11a Sofia Papagiannaki
            domain, allowed_paths, CLUSTER_NORMAL)
1774 5576e6dd Sofia Papagiannaki
        return [(path,
1775 5576e6dd Sofia Papagiannaki
                 self._build_metadata(props, user_defined_meta),
1776 7736e11a Sofia Papagiannaki
                 self.permissions.access_get(path)) for
1777 7736e11a Sofia Papagiannaki
                path, props, user_defined_meta in obj_list]
1778 5576e6dd Sofia Papagiannaki
1779 5576e6dd Sofia Papagiannaki
    # util functions
1780 5576e6dd Sofia Papagiannaki
1781 5576e6dd Sofia Papagiannaki
    def _build_metadata(self, props, user_defined=None,
1782 5576e6dd Sofia Papagiannaki
                        include_user_defined=True):
1783 5576e6dd Sofia Papagiannaki
        meta = {'bytes': props[self.SIZE],
1784 5576e6dd Sofia Papagiannaki
                'type': props[self.TYPE],
1785 5576e6dd Sofia Papagiannaki
                'hash': props[self.HASH],
1786 5576e6dd Sofia Papagiannaki
                'version': props[self.SERIAL],
1787 5576e6dd Sofia Papagiannaki
                'version_timestamp': props[self.MTIME],
1788 5576e6dd Sofia Papagiannaki
                'modified_by': props[self.MUSER],
1789 5576e6dd Sofia Papagiannaki
                'uuid': props[self.UUID],
1790 5576e6dd Sofia Papagiannaki
                'checksum': props[self.CHECKSUM]}
1791 29148653 Sofia Papagiannaki
        if include_user_defined and user_defined is not None:
1792 5576e6dd Sofia Papagiannaki
            meta.update(user_defined)
1793 5576e6dd Sofia Papagiannaki
        return meta
1794 5576e6dd Sofia Papagiannaki
1795 33af031c Sofia Papagiannaki
    def _exists(self, node):
1796 33af031c Sofia Papagiannaki
        try:
1797 33af031c Sofia Papagiannaki
            self._get_version(node)
1798 33af031c Sofia Papagiannaki
        except ItemNotExists:
1799 33af031c Sofia Papagiannaki
            return False
1800 33af031c Sofia Papagiannaki
        else:
1801 33af031c Sofia Papagiannaki
            return True
1802 3a5994a8 Sofia Papagiannaki
1803 3a5994a8 Sofia Papagiannaki
    def _unhexlify_hash(self, hash):
1804 3a5994a8 Sofia Papagiannaki
        try:
1805 3a5994a8 Sofia Papagiannaki
            return binascii.unhexlify(hash)
1806 3a5994a8 Sofia Papagiannaki
        except TypeError:
1807 3a5994a8 Sofia Papagiannaki
            raise InvalidHash(hash)