Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 83a3723e

History | View | Annotate | Download (70.1 kB)

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

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