Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (65.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 b336e6fa Georgios D. Tsoukalas
from synnefo.lib.quotaholder import QuotaholderClient
41 0307b47f Georgios D. Tsoukalas
42 19ddd41b Sofia Papagiannaki
from base import (DEFAULT_ACCOUNT_QUOTA, DEFAULT_CONTAINER_QUOTA,
43 19ddd41b Sofia Papagiannaki
                  DEFAULT_CONTAINER_VERSIONING, NotAllowedError, QuotaError,
44 19ddd41b Sofia Papagiannaki
                  BaseBackend, AccountExists, ContainerExists, AccountNotEmpty,
45 19ddd41b Sofia Papagiannaki
                  ContainerNotEmpty, ItemNotExists, VersionNotExists)
46 a9b3f29d Antony Chazapis
47 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
48 2715ade4 Sofia Papagiannaki
49 2715ade4 Sofia Papagiannaki
50 6e147ecc Antony Chazapis
class HashMap(list):
51 6e147ecc Antony Chazapis
52 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
53 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
54 6e147ecc Antony Chazapis
        self.blocksize = blocksize
55 6e147ecc Antony Chazapis
        self.blockhash = blockhash
56 6e147ecc Antony Chazapis
57 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
58 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
59 6e147ecc Antony Chazapis
        h.update(v)
60 6e147ecc Antony Chazapis
        return h.digest()
61 6e147ecc Antony Chazapis
62 6e147ecc Antony Chazapis
    def hash(self):
63 6e147ecc Antony Chazapis
        if len(self) == 0:
64 6e147ecc Antony Chazapis
            return self._hash_raw('')
65 6e147ecc Antony Chazapis
        if len(self) == 1:
66 6e147ecc Antony Chazapis
            return self.__getitem__(0)
67 6e147ecc Antony Chazapis
68 6e147ecc Antony Chazapis
        h = list(self)
69 6e147ecc Antony Chazapis
        s = 2
70 6e147ecc Antony Chazapis
        while s < len(h):
71 6e147ecc Antony Chazapis
            s = s * 2
72 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
73 6e147ecc Antony Chazapis
        while len(h) > 1:
74 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
75 6e147ecc Antony Chazapis
        return h[0]
76 5a96180b Antony Chazapis
77 228de81b Antony Chazapis
# Default modules and settings.
78 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
79 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
80 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
81 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
82 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
83 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
84 7f1f0464 Georgios D. Tsoukalas
DEFAULT_BLOCK_PARAMS = { 'mappool': None, 'blockpool': None }
85 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
86 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
87 4a105ce2 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_ALPHABET = ('0123456789'
88 4a105ce2 Sofia Papagiannaki
                               'abcdefghijklmnopqrstuvwxyz'
89 4a105ce2 Sofia Papagiannaki
                               'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
90 c77af544 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_SECURITY = 16
91 fa9cae7e Antony Chazapis
92 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
93 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
94 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
95 228de81b Antony Chazapis
96 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
97 44ad5860 Antony Chazapis
98 44ad5860 Antony Chazapis
inf = float('inf')
99 44ad5860 Antony Chazapis
100 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
101 bb4eafc6 Antony Chazapis
102 a9b3f29d Antony Chazapis
103 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
104 a9b3f29d Antony Chazapis
105 1c2fc0ff Antony Chazapis
106 a9b3f29d Antony Chazapis
def backend_method(func=None, autocommit=1):
107 a9b3f29d Antony Chazapis
    if func is None:
108 a9b3f29d Antony Chazapis
        def fn(func):
109 a9b3f29d Antony Chazapis
            return backend_method(func, autocommit)
110 a9b3f29d Antony Chazapis
        return fn
111 a9b3f29d Antony Chazapis
112 a9b3f29d Antony Chazapis
    if not autocommit:
113 a9b3f29d Antony Chazapis
        return func
114 2715ade4 Sofia Papagiannaki
115 a9b3f29d Antony Chazapis
    def fn(self, *args, **kw):
116 2c5363a0 Antony Chazapis
        self.wrapper.execute()
117 ecd01afd root
        serials = []
118 ecd01afd root
        self.serials = serials
119 85352a77 root
        self.messages = []
120 8313c681 root
121 a9b3f29d Antony Chazapis
        try:
122 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
123 39ef6f41 Antony Chazapis
            for m in self.messages:
124 39ef6f41 Antony Chazapis
                self.queue.send(*m)
125 5578064f root
            if serials:
126 613659fc Sofia Papagiannaki
                self.quotaholder_serials.insert_many(serials)
127 5578064f root
                self.quotaholder.accept_commission(
128 5578064f root
                            context     =   {},
129 5578064f root
                            clientkey   =   'pithos',
130 5578064f root
                            serials     =   serials)
131 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
132 a9b3f29d Antony Chazapis
            return ret
133 a9b3f29d Antony Chazapis
        except:
134 73fbe301 Sofia Papagiannaki
            if serials:
135 73fbe301 Sofia Papagiannaki
                self.quotaholder.reject_commission(
136 73fbe301 Sofia Papagiannaki
                            context     =   {},
137 73fbe301 Sofia Papagiannaki
                            clientkey   =   'pithos',
138 73fbe301 Sofia Papagiannaki
                            serials     =   serials)
139 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
140 a9b3f29d Antony Chazapis
            raise
141 a9b3f29d Antony Chazapis
    return fn
142 a9b3f29d Antony Chazapis
143 a9b3f29d Antony Chazapis
144 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
145 a9b3f29d Antony Chazapis
    """A modular backend.
146 2715ade4 Sofia Papagiannaki

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