Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (67.6 kB)

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