Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (71 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 cf7a6494 Sofia Papagiannaki
831 cf7a6494 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
832 cf7a6494 Sofia Papagiannaki
                                         lock_container=True)
833 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_metadata(
834 0f510652 Sofia Papagiannaki
            user, node, domain, meta, replace,
835 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=1)
836 0f510652 Sofia Papagiannaki
        self._apply_versioning(account, container, src_version_id,
837 0f510652 Sofia Papagiannaki
                               update_statistics_ancestors_depth=1)
838 5cc484e1 Antony Chazapis
        return dest_version_id
839 2715ade4 Sofia Papagiannaki
840 a9b3f29d Antony Chazapis
    @backend_method
841 a9b3f29d Antony Chazapis
    def get_object_permissions(self, user, account, container, name):
842 067cf1fc Antony Chazapis
        """Return the action allowed on the object, the path
843 067cf1fc Antony Chazapis
        from which the object gets its permissions from,
844 a9b3f29d Antony Chazapis
        along with a dictionary containing the permissions."""
845 2715ade4 Sofia Papagiannaki
846 2715ade4 Sofia Papagiannaki
        logger.debug("get_object_permissions: %s %s %s %s", user,
847 2715ade4 Sofia Papagiannaki
                     account, container, name)
848 067cf1fc Antony Chazapis
        allowed = 'write'
849 92da0e5a Antony Chazapis
        permissions_path = self._get_permissions_path(account, container, name)
850 067cf1fc Antony Chazapis
        if user != account:
851 92da0e5a Antony Chazapis
            if self.permissions.access_check(permissions_path, self.WRITE, user):
852 067cf1fc Antony Chazapis
                allowed = 'write'
853 92da0e5a Antony Chazapis
            elif self.permissions.access_check(permissions_path, self.READ, user):
854 067cf1fc Antony Chazapis
                allowed = 'read'
855 067cf1fc Antony Chazapis
            else:
856 067cf1fc Antony Chazapis
                raise NotAllowedError
857 92da0e5a Antony Chazapis
        self._lookup_object(account, container, name)
858 92da0e5a Antony Chazapis
        return (allowed, permissions_path, self.permissions.access_get(permissions_path))
859 2715ade4 Sofia Papagiannaki
860 a9b3f29d Antony Chazapis
    @backend_method
861 a9b3f29d Antony Chazapis
    def update_object_permissions(self, user, account, container, name, permissions):
862 a9b3f29d Antony Chazapis
        """Update the permissions associated with the object."""
863 2715ade4 Sofia Papagiannaki
864 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_permissions: %s %s %s %s %s",
865 2715ade4 Sofia Papagiannaki
                     user, account, container, name, permissions)
866 a9b3f29d Antony Chazapis
        if user != account:
867 a9b3f29d Antony Chazapis
            raise NotAllowedError
868 cf7a6494 Sofia Papagiannaki
        path = self._lookup_object(account, container, name,
869 cf7a6494 Sofia Papagiannaki
                                   lock_container=True)[0]
870 6f4bce7b Antony Chazapis
        self._check_permissions(path, permissions)
871 0f9d752c Antony Chazapis
        self.permissions.access_set(path, permissions)
872 2715ade4 Sofia Papagiannaki
        self._report_sharing_change(user, account, path, {'members':
873 2715ade4 Sofia Papagiannaki
                                    self.permissions.access_members(path)})
874 2715ade4 Sofia Papagiannaki
875 a9b3f29d Antony Chazapis
    @backend_method
876 a9b3f29d Antony Chazapis
    def get_object_public(self, user, account, container, name):
877 bb4eafc6 Antony Chazapis
        """Return the public id of the object if applicable."""
878 2715ade4 Sofia Papagiannaki
879 2715ade4 Sofia Papagiannaki
        logger.debug(
880 2715ade4 Sofia Papagiannaki
            "get_object_public: %s %s %s %s", user, account, container, name)
881 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
882 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
883 bb4eafc6 Antony Chazapis
        p = self.permissions.public_get(path)
884 bb4eafc6 Antony Chazapis
        return p
885 2715ade4 Sofia Papagiannaki
886 a9b3f29d Antony Chazapis
    @backend_method
887 a9b3f29d Antony Chazapis
    def update_object_public(self, user, account, container, name, public):
888 a9b3f29d Antony Chazapis
        """Update the public status of the object."""
889 2715ade4 Sofia Papagiannaki
890 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_public: %s %s %s %s %s", user,
891 2715ade4 Sofia Papagiannaki
                     account, container, name, public)
892 a9b3f29d Antony Chazapis
        self._can_write(user, account, container, name)
893 cf7a6494 Sofia Papagiannaki
        path = self._lookup_object(account, container, name,
894 cf7a6494 Sofia Papagiannaki
                                   lock_container=True)[0]
895 0f9d752c Antony Chazapis
        if not public:
896 0f9d752c Antony Chazapis
            self.permissions.public_unset(path)
897 0f9d752c Antony Chazapis
        else:
898 56f3c759 Sofia Papagiannaki
            self.permissions.public_set(
899 4a105ce2 Sofia Papagiannaki
                path, self.public_url_security, self.public_url_alphabet
900 56f3c759 Sofia Papagiannaki
            )
901 2715ade4 Sofia Papagiannaki
902 a9b3f29d Antony Chazapis
    @backend_method
903 a9b3f29d Antony Chazapis
    def get_object_hashmap(self, user, account, container, name, version=None):
904 a9b3f29d Antony Chazapis
        """Return the object's size and a list with partial hashes."""
905 2715ade4 Sofia Papagiannaki
906 2715ade4 Sofia Papagiannaki
        logger.debug("get_object_hashmap: %s %s %s %s %s", user,
907 2715ade4 Sofia Papagiannaki
                     account, container, name, version)
908 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
909 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
910 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
911 7ca7bb08 Antony Chazapis
        hashmap = self.store.map_get(binascii.unhexlify(props[self.HASH]))
912 2c5363a0 Antony Chazapis
        return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
913 2715ade4 Sofia Papagiannaki
914 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):
915 b9064632 Antony Chazapis
        if permissions is not None and user != account:
916 b9064632 Antony Chazapis
            raise NotAllowedError
917 b9064632 Antony Chazapis
        self._can_write(user, account, container, name)
918 b9064632 Antony Chazapis
        if permissions is not None:
919 b9064632 Antony Chazapis
            path = '/'.join((account, container, name))
920 b9064632 Antony Chazapis
            self._check_permissions(path, permissions)
921 2715ade4 Sofia Papagiannaki
922 b9064632 Antony Chazapis
        account_path, account_node = self._lookup_account(account, True)
923 2715ade4 Sofia Papagiannaki
        container_path, container_node = self._lookup_container(
924 2715ade4 Sofia Papagiannaki
            account, container)
925 fed9c5c7 Sofia Papagiannaki
926 2715ade4 Sofia Papagiannaki
        path, node = self._put_object_node(
927 2715ade4 Sofia Papagiannaki
            container_path, container_node, name)
928 0f510652 Sofia Papagiannaki
        pre_version_id, dest_version_id = self._put_version_duplicate(
929 0f510652 Sofia Papagiannaki
            user, node, src_node=src_node, size=size, type=type, hash=hash,
930 0f510652 Sofia Papagiannaki
            checksum=checksum, is_copy=is_copy,
931 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=1)
932 2715ade4 Sofia Papagiannaki
933 f9ea264b Antony Chazapis
        # Handle meta.
934 f9ea264b Antony Chazapis
        if src_version_id is None:
935 f9ea264b Antony Chazapis
            src_version_id = pre_version_id
936 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
937 4100e0ee Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace_meta)
938 2715ade4 Sofia Papagiannaki
939 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, pre_version_id,
940 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
941 813e42e5 Antony Chazapis
        size_delta = size - del_size
942 fed9c5c7 Sofia Papagiannaki
        if size_delta > 0:
943 fed9c5c7 Sofia Papagiannaki
            # Check account quota.
944 fed9c5c7 Sofia Papagiannaki
            if not self.using_external_quotaholder:
945 19ddd41b Sofia Papagiannaki
                account_quota = long(
946 19ddd41b Sofia Papagiannaki
                    self._get_policy(account_node, is_account_policy=True
947 19ddd41b Sofia Papagiannaki
                    )['quota']
948 19ddd41b Sofia Papagiannaki
                )
949 2e8edd42 Sofia Papagiannaki
                account_usage = self._get_statistics(account_node, compute=True)[1]
950 d3655326 Sofia Papagiannaki
                if (account_quota > 0 and account_usage > account_quota):
951 fed9c5c7 Sofia Papagiannaki
                    raise QuotaError(
952 fed9c5c7 Sofia Papagiannaki
                        'Account quota exceeded: limit: %s, usage: %s' % (
953 fed9c5c7 Sofia Papagiannaki
                            account_quota, account_usage
954 fed9c5c7 Sofia Papagiannaki
                        )
955 fed9c5c7 Sofia Papagiannaki
                    )
956 fed9c5c7 Sofia Papagiannaki
957 fed9c5c7 Sofia Papagiannaki
            # Check container quota.
958 fed9c5c7 Sofia Papagiannaki
            container_quota = long(
959 fed9c5c7 Sofia Papagiannaki
                self._get_policy(container_node, is_account_policy=False
960 fed9c5c7 Sofia Papagiannaki
                )['quota']
961 fed9c5c7 Sofia Papagiannaki
            )
962 fed9c5c7 Sofia Papagiannaki
            container_usage = self._get_statistics(container_node)[1]
963 fed9c5c7 Sofia Papagiannaki
            if (container_quota > 0 and container_usage > container_quota):
964 fed9c5c7 Sofia Papagiannaki
                # This must be executed in a transaction, so the version is
965 fed9c5c7 Sofia Papagiannaki
                # never created if it fails.
966 fed9c5c7 Sofia Papagiannaki
                raise QuotaError(
967 fed9c5c7 Sofia Papagiannaki
                    'Container quota exceeded: limit: %s, usage: %s' % (
968 fed9c5c7 Sofia Papagiannaki
                        container_quota, container_usage
969 fed9c5c7 Sofia Papagiannaki
                    )
970 fed9c5c7 Sofia Papagiannaki
                )
971 e20a751d Sofia Papagiannaki
972 388ea25f Sofia Papagiannaki
        self._report_size_change(user, account, size_delta,
973 7ed99da8 root
                                 {'action': 'object update', 'path': path,
974 b1dadd0e Sofia Papagiannaki
                                  'versions': ','.join([str(dest_version_id)])})
975 b9064632 Antony Chazapis
        if permissions is not None:
976 b9064632 Antony Chazapis
            self.permissions.access_set(path, permissions)
977 2715ade4 Sofia Papagiannaki
            self._report_sharing_change(user, account, path, {'members': self.permissions.access_members(path)})
978 2715ade4 Sofia Papagiannaki
979 39ef6f41 Antony Chazapis
        self._report_object_change(user, account, path, details={'version': dest_version_id, 'action': 'object update'})
980 f9ea264b Antony Chazapis
        return dest_version_id
981 2715ade4 Sofia Papagiannaki
982 a9b3f29d Antony Chazapis
    @backend_method
983 78348987 Sofia Papagiannaki
    def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta=None, replace_meta=False, permissions=None):
984 a9b3f29d Antony Chazapis
        """Create/update an object with the specified size and partial hashes."""
985 2715ade4 Sofia Papagiannaki
986 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_hashmap: %s %s %s %s %s %s %s %s", user,
987 2715ade4 Sofia Papagiannaki
                     account, container, name, size, type, hashmap, checksum)
988 78348987 Sofia Papagiannaki
        meta = meta or {}
989 2715ade4 Sofia Papagiannaki
        if size == 0:  # No such thing as an empty hashmap.
990 6d64339e Antony Chazapis
            hashmap = [self.put_block('')]
991 1c2fc0ff Antony Chazapis
        map = HashMap(self.block_size, self.hash_algorithm)
992 1c2fc0ff Antony Chazapis
        map.extend([binascii.unhexlify(x) for x in hashmap])
993 7ca7bb08 Antony Chazapis
        missing = self.store.block_search(map)
994 a9b3f29d Antony Chazapis
        if missing:
995 a9b3f29d Antony Chazapis
            ie = IndexError()
996 dd71f493 Antony Chazapis
            ie.data = [binascii.hexlify(x) for x in missing]
997 a9b3f29d Antony Chazapis
            raise ie
998 2715ade4 Sofia Papagiannaki
999 1c2fc0ff Antony Chazapis
        hash = map.hash()
1000 cf7a6494 Sofia Papagiannaki
        # _update_object_hash() locks destination path
1001 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)
1002 7ca7bb08 Antony Chazapis
        self.store.map_put(hash, map)
1003 02c4d2ba Antony Chazapis
        return dest_version_id
1004 2715ade4 Sofia Papagiannaki
1005 33b4e4a6 Antony Chazapis
    @backend_method
1006 33b4e4a6 Antony Chazapis
    def update_object_checksum(self, user, account, container, name, version, checksum):
1007 33b4e4a6 Antony Chazapis
        """Update an object's checksum."""
1008 2715ade4 Sofia Papagiannaki
1009 2715ade4 Sofia Papagiannaki
        logger.debug("update_object_checksum: %s %s %s %s %s %s",
1010 2715ade4 Sofia Papagiannaki
                     user, account, container, name, version, checksum)
1011 33b4e4a6 Antony Chazapis
        # Update objects with greater version and same hashmap and size (fix metadata updates).
1012 33b4e4a6 Antony Chazapis
        self._can_write(user, account, container, name)
1013 cf7a6494 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1014 cf7a6494 Sofia Papagiannaki
                                         lock_container=True)
1015 33b4e4a6 Antony Chazapis
        props = self._get_version(node, version)
1016 33b4e4a6 Antony Chazapis
        versions = self.node.node_get_versions(node)
1017 33b4e4a6 Antony Chazapis
        for x in versions:
1018 33b4e4a6 Antony Chazapis
            if x[self.SERIAL] >= int(version) and x[self.HASH] == props[self.HASH] and x[self.SIZE] == props[self.SIZE]:
1019 2715ade4 Sofia Papagiannaki
                self.node.version_put_property(
1020 2715ade4 Sofia Papagiannaki
                    x[self.SERIAL], 'checksum', checksum)
1021 2715ade4 Sofia Papagiannaki
1022 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):
1023 78348987 Sofia Papagiannaki
        dest_meta = dest_meta or {}
1024 4d15c94e Sofia Papagiannaki
        dest_version_ids = []
1025 79bb41b7 Antony Chazapis
        self._can_read(user, src_account, src_container, src_name)
1026 cf7a6494 Sofia Papagiannaki
        path, node = self._lookup_object(src_account, src_container, src_name,
1027 cf7a6494 Sofia Papagiannaki
                                         lock_container=True)
1028 1730b3bf chazapis
        # TODO: Will do another fetch of the properties in duplicate version...
1029 2715ade4 Sofia Papagiannaki
        props = self._get_version(
1030 2715ade4 Sofia Papagiannaki
            node, src_version)  # Check to see if source exists.
1031 b9064632 Antony Chazapis
        src_version_id = props[self.SERIAL]
1032 b9064632 Antony Chazapis
        hash = props[self.HASH]
1033 b9064632 Antony Chazapis
        size = props[self.SIZE]
1034 2715ade4 Sofia Papagiannaki
        is_copy = not is_move and (src_account, src_container, src_name) != (
1035 2715ade4 Sofia Papagiannaki
            dest_account, dest_container, dest_name)  # New uuid.
1036 cf7a6494 Sofia Papagiannaki
        # _update_object_hash() locks destination path
1037 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))
1038 3f767854 Sofia Papagiannaki
        if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
1039 2715ade4 Sofia Papagiannaki
            self._delete_object(user, src_account, src_container, src_name)
1040 2715ade4 Sofia Papagiannaki
1041 4d15c94e Sofia Papagiannaki
        if delimiter:
1042 2715ade4 Sofia Papagiannaki
            prefix = src_name + \
1043 2715ade4 Sofia Papagiannaki
                delimiter if not src_name.endswith(delimiter) else src_name
1044 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)
1045 2715ade4 Sofia Papagiannaki
            src_names.sort(key=lambda x: x[2])  # order by nodes
1046 4d15c94e Sofia Papagiannaki
            paths = [elem[0] for elem in src_names]
1047 4d15c94e Sofia Papagiannaki
            nodes = [elem[2] for elem in src_names]
1048 4d15c94e Sofia Papagiannaki
            # TODO: Will do another fetch of the properties in duplicate version...
1049 2715ade4 Sofia Papagiannaki
            props = self._get_versions(nodes)  # Check to see if source exists.
1050 2715ade4 Sofia Papagiannaki
1051 4d15c94e Sofia Papagiannaki
            for prop, path, node in zip(props, paths, nodes):
1052 4d15c94e Sofia Papagiannaki
                src_version_id = prop[self.SERIAL]
1053 4d15c94e Sofia Papagiannaki
                hash = prop[self.HASH]
1054 4d15c94e Sofia Papagiannaki
                vtype = prop[self.TYPE]
1055 07867f70 Sofia Papagiannaki
                size = prop[self.SIZE]
1056 2715ade4 Sofia Papagiannaki
                dest_prefix = dest_name + delimiter if not dest_name.endswith(
1057 2715ade4 Sofia Papagiannaki
                    delimiter) else dest_name
1058 4d15c94e Sofia Papagiannaki
                vdest_name = path.replace(prefix, dest_prefix, 1)
1059 cf7a6494 Sofia Papagiannaki
                # _update_object_hash() locks destination path
1060 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))
1061 3f767854 Sofia Papagiannaki
                if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
1062 2715ade4 Sofia Papagiannaki
                    self._delete_object(user, src_account, src_container, path)
1063 4d15c94e Sofia Papagiannaki
        return dest_version_ids[0] if len(dest_version_ids) == 1 else dest_version_ids
1064 2715ade4 Sofia Papagiannaki
1065 4d15c94e Sofia Papagiannaki
    @backend_method
1066 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):
1067 dff7b6f1 Sofia Papagiannaki
        """Copy an object's data and metadata."""
1068 2715ade4 Sofia Papagiannaki
1069 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)
1070 78348987 Sofia Papagiannaki
        meta = meta or {}
1071 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)
1072 46286f5f Antony Chazapis
        return dest_version_id
1073 2715ade4 Sofia Papagiannaki
1074 dff7b6f1 Sofia Papagiannaki
    @backend_method
1075 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):
1076 a9b3f29d Antony Chazapis
        """Move an object's data and metadata."""
1077 2715ade4 Sofia Papagiannaki
1078 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)
1079 78348987 Sofia Papagiannaki
        meta = meta or {}
1080 79bb41b7 Antony Chazapis
        if user != src_account:
1081 79bb41b7 Antony Chazapis
            raise NotAllowedError
1082 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)
1083 02c4d2ba Antony Chazapis
        return dest_version_id
1084 2715ade4 Sofia Papagiannaki
1085 4d15c94e Sofia Papagiannaki
    def _delete_object(self, user, account, container, name, until=None, delimiter=None):
1086 a9b3f29d Antony Chazapis
        if user != account:
1087 a9b3f29d Antony Chazapis
            raise NotAllowedError
1088 2715ade4 Sofia Papagiannaki
1089 cf7a6494 Sofia Papagiannaki
        # lookup object and lock container path also
1090 cf7a6494 Sofia Papagiannaki
        path, node = self._lookup_object(account, container, name,
1091 cf7a6494 Sofia Papagiannaki
                                         lock_container=True)
1092 cf7a6494 Sofia Papagiannaki
1093 a9b3f29d Antony Chazapis
        if until is not None:
1094 a9b3f29d Antony Chazapis
            path = '/'.join((account, container, name))
1095 c915d3bf Antony Chazapis
            node = self.node.node_lookup(path)
1096 c915d3bf Antony Chazapis
            if node is None:
1097 c915d3bf Antony Chazapis
                return
1098 813e42e5 Antony Chazapis
            hashes = []
1099 813e42e5 Antony Chazapis
            size = 0
1100 388ea25f Sofia Papagiannaki
            serials = []
1101 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_NORMAL,
1102 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1103 813e42e5 Antony Chazapis
            hashes += h
1104 813e42e5 Antony Chazapis
            size += s
1105 388ea25f Sofia Papagiannaki
            serials += v
1106 0f510652 Sofia Papagiannaki
            h, s, v = self.node.node_purge(node, until, CLUSTER_HISTORY,
1107 0f510652 Sofia Papagiannaki
                                           update_statistics_ancestors_depth=1)
1108 813e42e5 Antony Chazapis
            hashes += h
1109 0a92ff85 Sofia Papagiannaki
            if not self.free_versioning:
1110 0a92ff85 Sofia Papagiannaki
                size += s
1111 388ea25f Sofia Papagiannaki
            serials += v
1112 04230536 Antony Chazapis
            for h in hashes:
1113 04230536 Antony Chazapis
                self.store.map_delete(h)
1114 0f510652 Sofia Papagiannaki
            self.node.node_purge(node, until, CLUSTER_DELETED,
1115 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth=1)
1116 a9b3f29d Antony Chazapis
            try:
1117 c915d3bf Antony Chazapis
                props = self._get_version(node)
1118 a9b3f29d Antony Chazapis
            except NameError:
1119 0f9d752c Antony Chazapis
                self.permissions.access_clear(path)
1120 0a92ff85 Sofia Papagiannaki
            self._report_size_change(
1121 0a92ff85 Sofia Papagiannaki
                user, account, -size, {
1122 0a92ff85 Sofia Papagiannaki
                    'action': 'object purge',
1123 0a92ff85 Sofia Papagiannaki
                    'path': path,
1124 0a92ff85 Sofia Papagiannaki
                    'versions': ','.join(str(i) for i in serials)
1125 0a92ff85 Sofia Papagiannaki
                }
1126 0a92ff85 Sofia Papagiannaki
            )
1127 a9b3f29d Antony Chazapis
            return
1128 2715ade4 Sofia Papagiannaki
1129 0f510652 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1130 0f510652 Sofia Papagiannaki
            user, node, size=0, type='', hash=None, checksum='',
1131 0f510652 Sofia Papagiannaki
            cluster=CLUSTER_DELETED, update_statistics_ancestors_depth=1)
1132 0f510652 Sofia Papagiannaki
        del_size = self._apply_versioning(account, container, src_version_id,
1133 0f510652 Sofia Papagiannaki
                                          update_statistics_ancestors_depth=1)
1134 b1dadd0e Sofia Papagiannaki
        self._report_size_change(user, account, -del_size,
1135 b1dadd0e Sofia Papagiannaki
                                 {'action': 'object delete', 'path': path,
1136 b1dadd0e Sofia Papagiannaki
                                  'versions': ','.join([str(dest_version_id)])})
1137 2715ade4 Sofia Papagiannaki
        self._report_object_change(
1138 2715ade4 Sofia Papagiannaki
            user, account, path, details={'action': 'object delete'})
1139 0f9d752c Antony Chazapis
        self.permissions.access_clear(path)
1140 2715ade4 Sofia Papagiannaki
1141 4d15c94e Sofia Papagiannaki
        if delimiter:
1142 4d15c94e Sofia Papagiannaki
            prefix = name + delimiter if not name.endswith(delimiter) else name
1143 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)
1144 4d15c94e Sofia Papagiannaki
            paths = []
1145 4d15c94e Sofia Papagiannaki
            for t in src_names:
1146 2715ade4 Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
1147 2715ade4 Sofia Papagiannaki
                node = t[2]
1148 0f510652 Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(
1149 0f510652 Sofia Papagiannaki
                    user, node, size=0, type='', hash=None, checksum='',
1150 0f510652 Sofia Papagiannaki
                    cluster=CLUSTER_DELETED,
1151 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1152 2715ade4 Sofia Papagiannaki
                del_size = self._apply_versioning(
1153 0f510652 Sofia Papagiannaki
                    account, container, src_version_id,
1154 0f510652 Sofia Papagiannaki
                    update_statistics_ancestors_depth=1)
1155 b1dadd0e Sofia Papagiannaki
                self._report_size_change(user, account, -del_size,
1156 b1dadd0e Sofia Papagiannaki
                                         {'action': 'object delete',
1157 b1dadd0e Sofia Papagiannaki
                                          'path': path,
1158 b1dadd0e Sofia Papagiannaki
                                          'versions': ','.join([str(dest_version_id)])})
1159 2715ade4 Sofia Papagiannaki
                self._report_object_change(
1160 2715ade4 Sofia Papagiannaki
                    user, account, path, details={'action': 'object delete'})
1161 4d15c94e Sofia Papagiannaki
                paths.append(path)
1162 4d15c94e Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
1163 2715ade4 Sofia Papagiannaki
1164 4d15c94e Sofia Papagiannaki
    @backend_method
1165 4d15c94e Sofia Papagiannaki
    def delete_object(self, user, account, container, name, until=None, prefix='', delimiter=None):
1166 dff7b6f1 Sofia Papagiannaki
        """Delete/purge an object."""
1167 2715ade4 Sofia Papagiannaki
1168 2715ade4 Sofia Papagiannaki
        logger.debug("delete_object: %s %s %s %s %s %s %s", user,
1169 2715ade4 Sofia Papagiannaki
                     account, container, name, until, prefix, delimiter)
1170 4d15c94e Sofia Papagiannaki
        self._delete_object(user, account, container, name, until, delimiter)
1171 2715ade4 Sofia Papagiannaki
1172 dff7b6f1 Sofia Papagiannaki
    @backend_method
1173 62f915a1 Antony Chazapis
    def list_versions(self, user, account, container, name):
1174 62f915a1 Antony Chazapis
        """Return a list of all (version, version_timestamp) tuples for an object."""
1175 2715ade4 Sofia Papagiannaki
1176 2715ade4 Sofia Papagiannaki
        logger.debug(
1177 2715ade4 Sofia Papagiannaki
            "list_versions: %s %s %s %s", user, account, container, name)
1178 62f915a1 Antony Chazapis
        self._can_read(user, account, container, name)
1179 60b8a083 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
1180 97d45f69 Antony Chazapis
        versions = self.node.node_get_versions(node)
1181 97d45f69 Antony Chazapis
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if x[self.CLUSTER] != CLUSTER_DELETED]
1182 2715ade4 Sofia Papagiannaki
1183 bb4eafc6 Antony Chazapis
    @backend_method
1184 37bee317 Antony Chazapis
    def get_uuid(self, user, uuid):
1185 37bee317 Antony Chazapis
        """Return the (account, container, name) for the UUID given."""
1186 2715ade4 Sofia Papagiannaki
1187 fe2db49d Sofia Papagiannaki
        logger.debug("get_uuid: %s %s", user, uuid)
1188 2bbf1544 Georgios D. Tsoukalas
        info = self.node.latest_uuid(uuid, CLUSTER_NORMAL)
1189 37bee317 Antony Chazapis
        if info is None:
1190 37bee317 Antony Chazapis
            raise NameError
1191 37bee317 Antony Chazapis
        path, serial = info
1192 37bee317 Antony Chazapis
        account, container, name = path.split('/', 2)
1193 37bee317 Antony Chazapis
        self._can_read(user, account, container, name)
1194 37bee317 Antony Chazapis
        return (account, container, name)
1195 2715ade4 Sofia Papagiannaki
1196 37bee317 Antony Chazapis
    @backend_method
1197 bb4eafc6 Antony Chazapis
    def get_public(self, user, public):
1198 bb4eafc6 Antony Chazapis
        """Return the (account, container, name) for the public id given."""
1199 2715ade4 Sofia Papagiannaki
1200 fe2db49d Sofia Papagiannaki
        logger.debug("get_public: %s %s", user, public)
1201 56f3c759 Sofia Papagiannaki
        path = self.permissions.public_path(public)
1202 37bee317 Antony Chazapis
        if path is None:
1203 37bee317 Antony Chazapis
            raise NameError
1204 bb4eafc6 Antony Chazapis
        account, container, name = path.split('/', 2)
1205 bb4eafc6 Antony Chazapis
        self._can_read(user, account, container, name)
1206 bb4eafc6 Antony Chazapis
        return (account, container, name)
1207 2715ade4 Sofia Papagiannaki
1208 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
1209 a9b3f29d Antony Chazapis
    def get_block(self, hash):
1210 a9b3f29d Antony Chazapis
        """Return a block's data."""
1211 2715ade4 Sofia Papagiannaki
1212 a9b3f29d Antony Chazapis
        logger.debug("get_block: %s", hash)
1213 7ca7bb08 Antony Chazapis
        block = self.store.block_get(binascii.unhexlify(hash))
1214 7ca7bb08 Antony Chazapis
        if not block:
1215 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Block does not exist')
1216 7ca7bb08 Antony Chazapis
        return block
1217 2715ade4 Sofia Papagiannaki
1218 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
1219 a9b3f29d Antony Chazapis
    def put_block(self, data):
1220 60b8a083 Antony Chazapis
        """Store a block and return the hash."""
1221 2715ade4 Sofia Papagiannaki
1222 a9b3f29d Antony Chazapis
        logger.debug("put_block: %s", len(data))
1223 7ca7bb08 Antony Chazapis
        return binascii.hexlify(self.store.block_put(data))
1224 2715ade4 Sofia Papagiannaki
1225 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
1226 a9b3f29d Antony Chazapis
    def update_block(self, hash, data, offset=0):
1227 a9b3f29d Antony Chazapis
        """Update a known block and return the hash."""
1228 2715ade4 Sofia Papagiannaki
1229 a9b3f29d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
1230 a9b3f29d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
1231 a9b3f29d Antony Chazapis
            return self.put_block(data)
1232 7ca7bb08 Antony Chazapis
        h = self.store.block_update(binascii.unhexlify(hash), offset, data)
1233 a9b3f29d Antony Chazapis
        return binascii.hexlify(h)
1234 2715ade4 Sofia Papagiannaki
1235 44ad5860 Antony Chazapis
    # Path functions.
1236 2715ade4 Sofia Papagiannaki
1237 37bee317 Antony Chazapis
    def _generate_uuid(self):
1238 37bee317 Antony Chazapis
        return str(uuidlib.uuid4())
1239 2715ade4 Sofia Papagiannaki
1240 b9064632 Antony Chazapis
    def _put_object_node(self, path, parent, name):
1241 c915d3bf Antony Chazapis
        path = '/'.join((path, name))
1242 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1243 c915d3bf Antony Chazapis
        if node is None:
1244 2e8edd42 Sofia Papagiannaki
            node = self.node.node_create(parent, path)
1245 c915d3bf Antony Chazapis
        return path, node
1246 2715ade4 Sofia Papagiannaki
1247 0f510652 Sofia Papagiannaki
    def _put_path(self, user, parent, path,
1248 0f510652 Sofia Papagiannaki
                  update_statistics_ancestors_depth=None):
1249 62f915a1 Antony Chazapis
        node = self.node.node_create(parent, path)
1250 2715ade4 Sofia Papagiannaki
        self.node.version_create(node, None, 0, '', None, user,
1251 0f510652 Sofia Papagiannaki
                                 self._generate_uuid(), '', CLUSTER_NORMAL,
1252 0f510652 Sofia Papagiannaki
                                 update_statistics_ancestors_depth)
1253 62f915a1 Antony Chazapis
        return node
1254 2715ade4 Sofia Papagiannaki
1255 44ad5860 Antony Chazapis
    def _lookup_account(self, account, create=True):
1256 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
1257 44ad5860 Antony Chazapis
        if node is None and create:
1258 2715ade4 Sofia Papagiannaki
            node = self._put_path(
1259 0f510652 Sofia Papagiannaki
                account, self.ROOTNODE, account,
1260 0f510652 Sofia Papagiannaki
                update_statistics_ancestors_depth=-1)  # User is account.
1261 c915d3bf Antony Chazapis
        return account, node
1262 2715ade4 Sofia Papagiannaki
1263 44ad5860 Antony Chazapis
    def _lookup_container(self, account, container):
1264 c915d3bf Antony Chazapis
        path = '/'.join((account, container))
1265 985b9b09 Sofia Papagiannaki
        node = self.node.node_lookup(path, for_update=True)
1266 44ad5860 Antony Chazapis
        if node is None:
1267 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Container does not exist')
1268 c915d3bf Antony Chazapis
        return path, node
1269 2715ade4 Sofia Papagiannaki
1270 cf7a6494 Sofia Papagiannaki
    def _lookup_object(self, account, container, name, lock_container=False):
1271 cf7a6494 Sofia Papagiannaki
        if lock_container:
1272 cf7a6494 Sofia Papagiannaki
            self._lookup_container(account, container)
1273 cf7a6494 Sofia Papagiannaki
1274 c915d3bf Antony Chazapis
        path = '/'.join((account, container, name))
1275 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
1276 44ad5860 Antony Chazapis
        if node is None:
1277 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Object does not exist')
1278 c915d3bf Antony Chazapis
        return path, node
1279 2715ade4 Sofia Papagiannaki
1280 cf4a7a7b Sofia Papagiannaki
    def _lookup_objects(self, paths):
1281 7efc9f86 Sofia Papagiannaki
        nodes = self.node.node_lookup_bulk(paths)
1282 cf4a7a7b Sofia Papagiannaki
        return paths, nodes
1283 2715ade4 Sofia Papagiannaki
1284 44ad5860 Antony Chazapis
    def _get_properties(self, node, until=None):
1285 44ad5860 Antony Chazapis
        """Return properties until the timestamp given."""
1286 2715ade4 Sofia Papagiannaki
1287 44ad5860 Antony Chazapis
        before = until if until is not None else inf
1288 44ad5860 Antony Chazapis
        props = self.node.version_lookup(node, before, CLUSTER_NORMAL)
1289 44ad5860 Antony Chazapis
        if props is None and until is not None:
1290 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, before, CLUSTER_HISTORY)
1291 44ad5860 Antony Chazapis
        if props is None:
1292 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Path does not exist')
1293 44ad5860 Antony Chazapis
        return props
1294 2715ade4 Sofia Papagiannaki
1295 2e8edd42 Sofia Papagiannaki
    def _get_statistics(self, node, until=None, compute=False):
1296 62f915a1 Antony Chazapis
        """Return count, sum of size and latest timestamp of everything under node."""
1297 2715ade4 Sofia Papagiannaki
1298 2e8edd42 Sofia Papagiannaki
        if until is not None:
1299 62f915a1 Antony Chazapis
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1300 2e8edd42 Sofia Papagiannaki
        elif compute:
1301 2e8edd42 Sofia Papagiannaki
            stats = self.node.statistics_latest(node, except_cluster=CLUSTER_DELETED)
1302 2e8edd42 Sofia Papagiannaki
        else:
1303 2e8edd42 Sofia Papagiannaki
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1304 62f915a1 Antony Chazapis
        if stats is None:
1305 62f915a1 Antony Chazapis
            stats = (0, 0, 0)
1306 62f915a1 Antony Chazapis
        return stats
1307 2715ade4 Sofia Papagiannaki
1308 44ad5860 Antony Chazapis
    def _get_version(self, node, version=None):
1309 44ad5860 Antony Chazapis
        if version is None:
1310 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1311 44ad5860 Antony Chazapis
            if props is None:
1312 7efc9f86 Sofia Papagiannaki
                raise ItemNotExists('Object does not exist')
1313 44ad5860 Antony Chazapis
        else:
1314 07afd277 Antony Chazapis
            try:
1315 07afd277 Antony Chazapis
                version = int(version)
1316 07afd277 Antony Chazapis
            except ValueError:
1317 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1318 d2fc71c9 Sofia Papagiannaki
            props = self.node.version_get_properties(version, node=node)
1319 2c5363a0 Antony Chazapis
            if props is None or props[self.CLUSTER] == CLUSTER_DELETED:
1320 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1321 44ad5860 Antony Chazapis
        return props
1322 4d15c94e Sofia Papagiannaki
1323 7efc9f86 Sofia Papagiannaki
    def _get_versions(self, nodes):
1324 7efc9f86 Sofia Papagiannaki
        return self.node.version_lookup_bulk(nodes, inf, CLUSTER_NORMAL)
1325 2715ade4 Sofia Papagiannaki
1326 0f510652 Sofia Papagiannaki
    def _put_version_duplicate(self, user, node, src_node=None, size=None,
1327 0f510652 Sofia Papagiannaki
                               type=None, hash=None, checksum=None,
1328 0f510652 Sofia Papagiannaki
                               cluster=CLUSTER_NORMAL, is_copy=False,
1329 0f510652 Sofia Papagiannaki
                               update_statistics_ancestors_depth=None):
1330 b9064632 Antony Chazapis
        """Create a new version of the node."""
1331 2715ade4 Sofia Papagiannaki
1332 2715ade4 Sofia Papagiannaki
        props = self.node.version_lookup(
1333 2715ade4 Sofia Papagiannaki
            node if src_node is None else src_node, inf, CLUSTER_NORMAL)
1334 b9064632 Antony Chazapis
        if props is not None:
1335 b9064632 Antony Chazapis
            src_version_id = props[self.SERIAL]
1336 b9064632 Antony Chazapis
            src_hash = props[self.HASH]
1337 b9064632 Antony Chazapis
            src_size = props[self.SIZE]
1338 66ce2ca5 Antony Chazapis
            src_type = props[self.TYPE]
1339 33b4e4a6 Antony Chazapis
            src_checksum = props[self.CHECKSUM]
1340 44ad5860 Antony Chazapis
        else:
1341 b9064632 Antony Chazapis
            src_version_id = None
1342 b9064632 Antony Chazapis
            src_hash = None
1343 b9064632 Antony Chazapis
            src_size = 0
1344 66ce2ca5 Antony Chazapis
            src_type = ''
1345 33b4e4a6 Antony Chazapis
            src_checksum = ''
1346 2715ade4 Sofia Papagiannaki
        if size is None:  # Set metadata.
1347 2715ade4 Sofia Papagiannaki
            hash = src_hash  # This way hash can be set to None (account or container).
1348 b9064632 Antony Chazapis
            size = src_size
1349 66ce2ca5 Antony Chazapis
        if type is None:
1350 66ce2ca5 Antony Chazapis
            type = src_type
1351 33b4e4a6 Antony Chazapis
        if checksum is None:
1352 33b4e4a6 Antony Chazapis
            checksum = src_checksum
1353 2715ade4 Sofia Papagiannaki
        uuid = self._generate_uuid(
1354 2715ade4 Sofia Papagiannaki
        ) if (is_copy or src_version_id is None) else props[self.UUID]
1355 2715ade4 Sofia Papagiannaki
1356 1730b3bf chazapis
        if src_node is None:
1357 1730b3bf chazapis
            pre_version_id = src_version_id
1358 1730b3bf chazapis
        else:
1359 1730b3bf chazapis
            pre_version_id = None
1360 1730b3bf chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1361 1730b3bf chazapis
            if props is not None:
1362 1730b3bf chazapis
                pre_version_id = props[self.SERIAL]
1363 1730b3bf chazapis
        if pre_version_id is not None:
1364 0f510652 Sofia Papagiannaki
            self.node.version_recluster(pre_version_id, CLUSTER_HISTORY,
1365 0f510652 Sofia Papagiannaki
                                        update_statistics_ancestors_depth)
1366 2715ade4 Sofia Papagiannaki
1367 0f510652 Sofia Papagiannaki
        dest_version_id, mtime = self.node.version_create(
1368 0f510652 Sofia Papagiannaki
            node, hash, size, type, src_version_id, user, uuid, checksum,
1369 0f510652 Sofia Papagiannaki
            cluster, update_statistics_ancestors_depth)
1370 4100e0ee Sofia Papagiannaki
1371 4100e0ee Sofia Papagiannaki
        self.node.attribute_unset_is_latest(node, dest_version_id)
1372 4100e0ee Sofia Papagiannaki
1373 1730b3bf chazapis
        return pre_version_id, dest_version_id
1374 2715ade4 Sofia Papagiannaki
1375 4100e0ee Sofia Papagiannaki
    def _put_metadata_duplicate(self, src_version_id, dest_version_id, domain,
1376 4100e0ee Sofia Papagiannaki
                                node, meta, replace=False):
1377 4819d34f Antony Chazapis
        if src_version_id is not None:
1378 4819d34f Antony Chazapis
            self.node.attribute_copy(src_version_id, dest_version_id)
1379 4819d34f Antony Chazapis
        if not replace:
1380 2715ade4 Sofia Papagiannaki
            self.node.attribute_del(dest_version_id, domain, (
1381 2715ade4 Sofia Papagiannaki
                k for k, v in meta.iteritems() if v == ''))
1382 4100e0ee Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, (
1383 2715ade4 Sofia Papagiannaki
                (k, v) for k, v in meta.iteritems() if v != ''))
1384 4819d34f Antony Chazapis
        else:
1385 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain)
1386 4100e0ee Sofia Papagiannaki
            self.node.attribute_set(dest_version_id, domain, node, ((
1387 2715ade4 Sofia Papagiannaki
                k, v) for k, v in meta.iteritems()))
1388 2715ade4 Sofia Papagiannaki
1389 0f510652 Sofia Papagiannaki
    def _put_metadata(self, user, node, domain, meta, replace=False,
1390 0f510652 Sofia Papagiannaki
                      update_statistics_ancestors_depth=None):
1391 44ad5860 Antony Chazapis
        """Create a new version and store metadata."""
1392 2715ade4 Sofia Papagiannaki
1393 2715ade4 Sofia Papagiannaki
        src_version_id, dest_version_id = self._put_version_duplicate(
1394 0f510652 Sofia Papagiannaki
            user, node,
1395 0f510652 Sofia Papagiannaki
            update_statistics_ancestors_depth=update_statistics_ancestors_depth)
1396 2715ade4 Sofia Papagiannaki
        self._put_metadata_duplicate(
1397 4100e0ee Sofia Papagiannaki
            src_version_id, dest_version_id, domain, node, meta, replace)
1398 5cc484e1 Antony Chazapis
        return src_version_id, dest_version_id
1399 2715ade4 Sofia Papagiannaki
1400 60b8a083 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
1401 60b8a083 Antony Chazapis
        start = 0
1402 60b8a083 Antony Chazapis
        if marker:
1403 60b8a083 Antony Chazapis
            try:
1404 60b8a083 Antony Chazapis
                start = listing.index(marker) + 1
1405 60b8a083 Antony Chazapis
            except ValueError:
1406 60b8a083 Antony Chazapis
                pass
1407 60b8a083 Antony Chazapis
        if not limit or limit > 10000:
1408 60b8a083 Antony Chazapis
            limit = 10000
1409 60b8a083 Antony Chazapis
        return start, limit
1410 2715ade4 Sofia Papagiannaki
1411 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):
1412 78348987 Sofia Papagiannaki
        keys = keys or []
1413 78348987 Sofia Papagiannaki
        allowed = allowed or []
1414 60b8a083 Antony Chazapis
        cont_prefix = path + '/'
1415 60b8a083 Antony Chazapis
        prefix = cont_prefix + prefix
1416 60b8a083 Antony Chazapis
        start = cont_prefix + marker if marker else None
1417 60b8a083 Antony Chazapis
        before = until if until is not None else inf
1418 4819d34f Antony Chazapis
        filterq = keys if domain else []
1419 7ff57991 Antony Chazapis
        sizeq = size_range
1420 2715ade4 Sofia Papagiannaki
1421 371d907a Antony Chazapis
        objects, prefixes = self.node.latest_version_list(parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED, allowed, domain, filterq, sizeq, all_props)
1422 60b8a083 Antony Chazapis
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1423 43be9afd Sofia Papagiannaki
        objects.sort(key=lambda x: x[0])
1424 371d907a Antony Chazapis
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
1425 cf4a7a7b Sofia Papagiannaki
        return objects
1426 2715ade4 Sofia Papagiannaki
1427 813e42e5 Antony Chazapis
    # Reporting functions.
1428 2715ade4 Sofia Papagiannaki
1429 78348987 Sofia Papagiannaki
    def _report_size_change(self, user, account, size, details=None):
1430 78348987 Sofia Papagiannaki
        details = details or {}
1431 78348987 Sofia Papagiannaki
1432 8ed3f04c Sofia Papagiannaki
        if size == 0:
1433 8ed3f04c Sofia Papagiannaki
            return
1434 8ed3f04c Sofia Papagiannaki
1435 813e42e5 Antony Chazapis
        account_node = self._lookup_account(account, True)[1]
1436 2e8edd42 Sofia Papagiannaki
        total = self._get_statistics(account_node, compute=True)[1]
1437 813e42e5 Antony Chazapis
        details.update({'user': user, 'total': total})
1438 2715ade4 Sofia Papagiannaki
        logger.debug(
1439 2715ade4 Sofia Papagiannaki
            "_report_size_change: %s %s %s %s", user, account, size, details)
1440 6eb0de11 Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',),
1441 7ed99da8 root
                              account, QUEUE_INSTANCE_ID, 'diskspace',
1442 7ed99da8 root
                              float(size), details))
1443 7ed99da8 root
1444 c846fad1 Sofia Papagiannaki
        if not self.using_external_quotaholder:
1445 73fbe301 Sofia Papagiannaki
            return
1446 7f1f0464 Georgios D. Tsoukalas
1447 ccfd4e44 Sofia Papagiannaki
        try:
1448 b17e5550 Giorgos Korfiatis
            name = details['path'] if 'path' in details else ''
1449 16f2673e Sofia Papagiannaki
            serial = self.astakosclient.issue_one_commission(
1450 16f2673e Sofia Papagiannaki
                token=self.service_token,
1451 b17e5550 Giorgos Korfiatis
                holder=account,
1452 b17e5550 Giorgos Korfiatis
                source=DEFAULT_SOURCE,
1453 b17e5550 Giorgos Korfiatis
                provisions={'pithos.diskspace': size},
1454 b17e5550 Giorgos Korfiatis
                name=name
1455 b17e5550 Giorgos Korfiatis
                )
1456 ccfd4e44 Sofia Papagiannaki
        except BaseException, e:
1457 ccfd4e44 Sofia Papagiannaki
            raise QuotaError(e)
1458 ccfd4e44 Sofia Papagiannaki
        else:
1459 ccfd4e44 Sofia Papagiannaki
            self.serials.append(serial)
1460 0307b47f Georgios D. Tsoukalas
1461 78348987 Sofia Papagiannaki
    def _report_object_change(self, user, account, path, details=None):
1462 78348987 Sofia Papagiannaki
        details = details or {}
1463 b82d3277 Sofia Papagiannaki
        details.update({'user': user})
1464 2715ade4 Sofia Papagiannaki
        logger.debug("_report_object_change: %s %s %s %s", user,
1465 2715ade4 Sofia Papagiannaki
                     account, path, details)
1466 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',),
1467 b1dadd0e Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'object', path, details))
1468 2715ade4 Sofia Papagiannaki
1469 78348987 Sofia Papagiannaki
    def _report_sharing_change(self, user, account, path, details=None):
1470 2715ade4 Sofia Papagiannaki
        logger.debug("_report_permissions_change: %s %s %s %s",
1471 2715ade4 Sofia Papagiannaki
                     user, account, path, details)
1472 78348987 Sofia Papagiannaki
        details = details or {}
1473 a74ba506 Sofia Papagiannaki
        details.update({'user': user})
1474 f4fbb0fa Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',),
1475 b1dadd0e Sofia Papagiannaki
                              account, QUEUE_INSTANCE_ID, 'sharing', path, details))
1476 2715ade4 Sofia Papagiannaki
1477 60b8a083 Antony Chazapis
    # Policy functions.
1478 2715ade4 Sofia Papagiannaki
1479 19ddd41b Sofia Papagiannaki
    def _check_policy(self, policy, is_account_policy=True):
1480 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1481 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1482 60b8a083 Antony Chazapis
        for k in policy.keys():
1483 60b8a083 Antony Chazapis
            if policy[k] == '':
1484 19ddd41b Sofia Papagiannaki
                policy[k] = default_policy.get(k)
1485 60b8a083 Antony Chazapis
        for k, v in policy.iteritems():
1486 60b8a083 Antony Chazapis
            if k == 'quota':
1487 2715ade4 Sofia Papagiannaki
                q = int(v)  # May raise ValueError.
1488 60b8a083 Antony Chazapis
                if q < 0:
1489 60b8a083 Antony Chazapis
                    raise ValueError
1490 60b8a083 Antony Chazapis
            elif k == 'versioning':
1491 5cc484e1 Antony Chazapis
                if v not in ['auto', 'none']:
1492 60b8a083 Antony Chazapis
                    raise ValueError
1493 60b8a083 Antony Chazapis
            else:
1494 60b8a083 Antony Chazapis
                raise ValueError
1495 2715ade4 Sofia Papagiannaki
1496 19ddd41b Sofia Papagiannaki
    def _put_policy(self, node, policy, replace, is_account_policy=True):
1497 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1498 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1499 b2832c6a Antony Chazapis
        if replace:
1500 19ddd41b Sofia Papagiannaki
            for k, v in default_policy.iteritems():
1501 b2832c6a Antony Chazapis
                if k not in policy:
1502 b2832c6a Antony Chazapis
                    policy[k] = v
1503 b2832c6a Antony Chazapis
        self.node.policy_set(node, policy)
1504 2715ade4 Sofia Papagiannaki
1505 19ddd41b Sofia Papagiannaki
    def _get_policy(self, node, is_account_policy=True):
1506 19ddd41b Sofia Papagiannaki
        default_policy = self.default_account_policy \
1507 19ddd41b Sofia Papagiannaki
            if is_account_policy else self.default_container_policy
1508 19ddd41b Sofia Papagiannaki
        policy = default_policy.copy()
1509 b9064632 Antony Chazapis
        policy.update(self.node.policy_get(node))
1510 b9064632 Antony Chazapis
        return policy
1511 2715ade4 Sofia Papagiannaki
1512 0f510652 Sofia Papagiannaki
    def _apply_versioning(self, account, container, version_id,
1513 0f510652 Sofia Papagiannaki
                          update_statistics_ancestors_depth=None):
1514 813e42e5 Antony Chazapis
        """Delete the provided version if such is the policy.
1515 813e42e5 Antony Chazapis
           Return size of object removed.
1516 813e42e5 Antony Chazapis
        """
1517 2715ade4 Sofia Papagiannaki
1518 5cc484e1 Antony Chazapis
        if version_id is None:
1519 813e42e5 Antony Chazapis
            return 0
1520 5cc484e1 Antony Chazapis
        path, node = self._lookup_container(account, container)
1521 19ddd41b Sofia Papagiannaki
        versioning = self._get_policy(
1522 19ddd41b Sofia Papagiannaki
            node, is_account_policy=False)['versioning']
1523 1dd34bdd Sofia Papagiannaki
        if versioning != 'auto':
1524 0f510652 Sofia Papagiannaki
            hash, size = self.node.version_remove(
1525 0f510652 Sofia Papagiannaki
                version_id, update_statistics_ancestors_depth)
1526 5161c672 Antony Chazapis
            self.store.map_delete(hash)
1527 813e42e5 Antony Chazapis
            return size
1528 1dd34bdd Sofia Papagiannaki
        elif self.free_versioning:
1529 fff37615 Sofia Papagiannaki
            return self.node.version_get_properties(
1530 fff37615 Sofia Papagiannaki
                version_id, keys=('size',))[0]
1531 813e42e5 Antony Chazapis
        return 0
1532 2715ade4 Sofia Papagiannaki
1533 a9b3f29d Antony Chazapis
    # Access control functions.
1534 2715ade4 Sofia Papagiannaki
1535 a9b3f29d Antony Chazapis
    def _check_groups(self, groups):
1536 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in groups')
1537 a9b3f29d Antony Chazapis
        pass
1538 2715ade4 Sofia Papagiannaki
1539 a9b3f29d Antony Chazapis
    def _check_permissions(self, path, permissions):
1540 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in permissions')
1541 5e068361 Antony Chazapis
        pass
1542 2715ade4 Sofia Papagiannaki
1543 92da0e5a Antony Chazapis
    def _get_formatted_paths(self, paths):
1544 92da0e5a Antony Chazapis
        formatted = []
1545 92da0e5a Antony Chazapis
        for p in paths:
1546 92da0e5a Antony Chazapis
            node = self.node.node_lookup(p)
1547 a8b82467 Sofia Papagiannaki
            props = None
1548 92da0e5a Antony Chazapis
            if node is not None:
1549 92da0e5a Antony Chazapis
                props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1550 92da0e5a Antony Chazapis
            if props is not None:
1551 692485cc Antony Chazapis
                if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1552 d57eaad4 Antony Chazapis
                    formatted.append((p.rstrip('/') + '/', self.MATCH_PREFIX))
1553 d57eaad4 Antony Chazapis
                formatted.append((p, self.MATCH_EXACT))
1554 92da0e5a Antony Chazapis
        return formatted
1555 2715ade4 Sofia Papagiannaki
1556 5e068361 Antony Chazapis
    def _get_permissions_path(self, account, container, name):
1557 5e068361 Antony Chazapis
        path = '/'.join((account, container, name))
1558 5e068361 Antony Chazapis
        permission_paths = self.permissions.access_inherit(path)
1559 5e068361 Antony Chazapis
        permission_paths.sort()
1560 5e068361 Antony Chazapis
        permission_paths.reverse()
1561 5e068361 Antony Chazapis
        for p in permission_paths:
1562 5e068361 Antony Chazapis
            if p == path:
1563 5e068361 Antony Chazapis
                return p
1564 5e068361 Antony Chazapis
            else:
1565 71dbc012 Antony Chazapis
                if p.count('/') < 2:
1566 71dbc012 Antony Chazapis
                    continue
1567 92da0e5a Antony Chazapis
                node = self.node.node_lookup(p)
1568 d62295ba Sofia Papagiannaki
                props = None
1569 92da0e5a Antony Chazapis
                if node is not None:
1570 92da0e5a Antony Chazapis
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1571 92da0e5a Antony Chazapis
                if props is not None:
1572 692485cc Antony Chazapis
                    if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1573 5e068361 Antony Chazapis
                        return p
1574 5e068361 Antony Chazapis
        return None
1575 2715ade4 Sofia Papagiannaki
1576 6f4bce7b Antony Chazapis
    def _can_read(self, user, account, container, name):
1577 a9b3f29d Antony Chazapis
        if user == account:
1578 a9b3f29d Antony Chazapis
            return True
1579 a9b3f29d Antony Chazapis
        path = '/'.join((account, container, name))
1580 aeb2b64f Antony Chazapis
        if self.permissions.public_get(path) is not None:
1581 71dbc012 Antony Chazapis
            return True
1582 5e068361 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1583 71dbc012 Antony Chazapis
        if not path:
1584 71dbc012 Antony Chazapis
            raise NotAllowedError
1585 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.READ, user) and not self.permissions.access_check(path, self.WRITE, user):
1586 a9b3f29d Antony Chazapis
            raise NotAllowedError
1587 2715ade4 Sofia Papagiannaki
1588 a9b3f29d Antony Chazapis
    def _can_write(self, user, account, container, name):
1589 6f4bce7b Antony Chazapis
        if user == account:
1590 6f4bce7b Antony Chazapis
            return True
1591 6f4bce7b Antony Chazapis
        path = '/'.join((account, container, name))
1592 71dbc012 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1593 71dbc012 Antony Chazapis
        if not path:
1594 71dbc012 Antony Chazapis
            raise NotAllowedError
1595 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.WRITE, user):
1596 a9b3f29d Antony Chazapis
            raise NotAllowedError
1597 2715ade4 Sofia Papagiannaki
1598 a9b3f29d Antony Chazapis
    def _allowed_accounts(self, user):
1599 a9b3f29d Antony Chazapis
        allow = set()
1600 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user):
1601 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 1)[0])
1602 a9b3f29d Antony Chazapis
        return sorted(allow)
1603 2715ade4 Sofia Papagiannaki
1604 a9b3f29d Antony Chazapis
    def _allowed_containers(self, user, account):
1605 a9b3f29d Antony Chazapis
        allow = set()
1606 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user, account):
1607 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 2)[1])
1608 a9b3f29d Antony Chazapis
        return sorted(allow)
1609 5576e6dd Sofia Papagiannaki
1610 5576e6dd Sofia Papagiannaki
    # Domain functions
1611 5576e6dd Sofia Papagiannaki
1612 e77b7a99 Sofia Papagiannaki
    @backend_method
1613 e77b7a99 Sofia Papagiannaki
    def get_domain_objects(self, domain, user=None):
1614 18d46d23 Sofia Papagiannaki
        allowed_paths = self.permissions.access_list_paths(
1615 18d46d23 Sofia Papagiannaki
            user, include_owned=user is not None, include_containers=False)
1616 8b365874 Sofia Papagiannaki
        if not allowed_paths:
1617 8b365874 Sofia Papagiannaki
            return []
1618 8b365874 Sofia Papagiannaki
        obj_list = self.node.domain_object_list(
1619 8b365874 Sofia Papagiannaki
            domain, allowed_paths, CLUSTER_NORMAL)
1620 5576e6dd Sofia Papagiannaki
        return [(path,
1621 5576e6dd Sofia Papagiannaki
                 self._build_metadata(props, user_defined_meta),
1622 8b365874 Sofia Papagiannaki
                 self.permissions.access_get(path)) for
1623 8b365874 Sofia Papagiannaki
                path, props, user_defined_meta in obj_list]
1624 5576e6dd Sofia Papagiannaki
1625 5576e6dd Sofia Papagiannaki
    # util functions
1626 5576e6dd Sofia Papagiannaki
1627 5576e6dd Sofia Papagiannaki
    def _build_metadata(self, props, user_defined=None,
1628 5576e6dd Sofia Papagiannaki
                        include_user_defined=True):
1629 5576e6dd Sofia Papagiannaki
        meta = {'bytes': props[self.SIZE],
1630 5576e6dd Sofia Papagiannaki
                'type': props[self.TYPE],
1631 5576e6dd Sofia Papagiannaki
                'hash': props[self.HASH],
1632 5576e6dd Sofia Papagiannaki
                'version': props[self.SERIAL],
1633 5576e6dd Sofia Papagiannaki
                'version_timestamp': props[self.MTIME],
1634 5576e6dd Sofia Papagiannaki
                'modified_by': props[self.MUSER],
1635 5576e6dd Sofia Papagiannaki
                'uuid': props[self.UUID],
1636 5576e6dd Sofia Papagiannaki
                'checksum': props[self.CHECKSUM]}
1637 5576e6dd Sofia Papagiannaki
        if include_user_defined and user_defined != None:
1638 5576e6dd Sofia Papagiannaki
            meta.update(user_defined)
1639 5576e6dd Sofia Papagiannaki
        return meta
1640 5576e6dd Sofia Papagiannaki
1641 5576e6dd Sofia Papagiannaki
    def _has_read_access(self, user, path):
1642 5576e6dd Sofia Papagiannaki
        try:
1643 5576e6dd Sofia Papagiannaki
            account, container, object = path.split('/', 2)
1644 5576e6dd Sofia Papagiannaki
        except ValueError:
1645 5576e6dd Sofia Papagiannaki
            raise ValueError('Invalid object path')
1646 5576e6dd Sofia Papagiannaki
1647 5576e6dd Sofia Papagiannaki
        assert isinstance(user, basestring), "Invalid user"
1648 5576e6dd Sofia Papagiannaki
1649 5576e6dd Sofia Papagiannaki
        try:
1650 5576e6dd Sofia Papagiannaki
            self._can_read(user, account, container, object)
1651 5576e6dd Sofia Papagiannaki
        except NotAllowedError:
1652 5576e6dd Sofia Papagiannaki
            return False
1653 5576e6dd Sofia Papagiannaki
        else:
1654 5576e6dd Sofia Papagiannaki
            return True