Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 8570576b

History | View | Annotate | Download (67.4 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 9b264076 Sofia Papagiannaki
128 9b264076 Sofia Papagiannaki
                # commit to ensure that the serials are registered
129 9b264076 Sofia Papagiannaki
                # even if accept commission fails
130 9b264076 Sofia Papagiannaki
                self.wrapper.commit()
131 9b264076 Sofia Papagiannaki
                self.wrapper.execute()
132 9b264076 Sofia Papagiannaki
133 5578064f root
                self.quotaholder.accept_commission(
134 5578064f root
                            context     =   {},
135 5578064f root
                            clientkey   =   'pithos',
136 5578064f root
                            serials     =   serials)
137 9b264076 Sofia Papagiannaki
138 9b264076 Sofia Papagiannaki
                self.quotaholder_serials.delete_many(serials)
139 9b264076 Sofia Papagiannaki
140 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
141 a9b3f29d Antony Chazapis
            return ret
142 a9b3f29d Antony Chazapis
        except:
143 73fbe301 Sofia Papagiannaki
            if serials:
144 73fbe301 Sofia Papagiannaki
                self.quotaholder.reject_commission(
145 73fbe301 Sofia Papagiannaki
                            context     =   {},
146 73fbe301 Sofia Papagiannaki
                            clientkey   =   'pithos',
147 73fbe301 Sofia Papagiannaki
                            serials     =   serials)
148 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
149 a9b3f29d Antony Chazapis
            raise
150 a9b3f29d Antony Chazapis
    return fn
151 a9b3f29d Antony Chazapis
152 a9b3f29d Antony Chazapis
153 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
154 a9b3f29d Antony Chazapis
    """A modular backend.
155 2715ade4 Sofia Papagiannaki

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