Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 8ee4ec50

History | View | Annotate | Download (57.8 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 a9b3f29d Antony Chazapis
# 
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 a9b3f29d Antony Chazapis
# 
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 a9b3f29d Antony Chazapis
# 
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 a9b3f29d Antony Chazapis
# 
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 a9b3f29d Antony Chazapis
# 
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 a9b3f29d Antony Chazapis
import os
36 a9b3f29d Antony Chazapis
import time
37 37bee317 Antony Chazapis
import uuid as uuidlib
38 a9b3f29d Antony Chazapis
import logging
39 6e147ecc Antony Chazapis
import hashlib
40 a9b3f29d Antony Chazapis
import binascii
41 a9b3f29d Antony Chazapis
42 7efc9f86 Sofia Papagiannaki
from base import DEFAULT_QUOTA, DEFAULT_VERSIONING, NotAllowedError, QuotaError, BaseBackend, \
43 7efc9f86 Sofia Papagiannaki
    AccountExists, ContainerExists, AccountNotEmpty, ContainerNotEmpty, ItemNotExists, VersionNotExists
44 a9b3f29d Antony Chazapis
45 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
46 6e147ecc Antony Chazapis
class HashMap(list):
47 6e147ecc Antony Chazapis
48 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
49 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
50 6e147ecc Antony Chazapis
        self.blocksize = blocksize
51 6e147ecc Antony Chazapis
        self.blockhash = blockhash
52 6e147ecc Antony Chazapis
53 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
54 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
55 6e147ecc Antony Chazapis
        h.update(v)
56 6e147ecc Antony Chazapis
        return h.digest()
57 6e147ecc Antony Chazapis
58 6e147ecc Antony Chazapis
    def hash(self):
59 6e147ecc Antony Chazapis
        if len(self) == 0:
60 6e147ecc Antony Chazapis
            return self._hash_raw('')
61 6e147ecc Antony Chazapis
        if len(self) == 1:
62 6e147ecc Antony Chazapis
            return self.__getitem__(0)
63 6e147ecc Antony Chazapis
64 6e147ecc Antony Chazapis
        h = list(self)
65 6e147ecc Antony Chazapis
        s = 2
66 6e147ecc Antony Chazapis
        while s < len(h):
67 6e147ecc Antony Chazapis
            s = s * 2
68 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
69 6e147ecc Antony Chazapis
        while len(h) > 1:
70 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
71 6e147ecc Antony Chazapis
        return h[0]
72 5a96180b Antony Chazapis
73 228de81b Antony Chazapis
# Default modules and settings.
74 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
75 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
76 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
77 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
78 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
79 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
80 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_CONNECTION = 'rabbitmq://guest:guest@localhost:5672/pithos'
81 fa9cae7e Antony Chazapis
82 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
83 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
84 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
85 228de81b Antony Chazapis
86 44ad5860 Antony Chazapis
( CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED ) = range(3)
87 44ad5860 Antony Chazapis
88 44ad5860 Antony Chazapis
inf = float('inf')
89 44ad5860 Antony Chazapis
90 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
91 bb4eafc6 Antony Chazapis
92 a9b3f29d Antony Chazapis
93 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
94 a9b3f29d Antony Chazapis
95 1c2fc0ff Antony Chazapis
96 a9b3f29d Antony Chazapis
def backend_method(func=None, autocommit=1):
97 a9b3f29d Antony Chazapis
    if func is None:
98 a9b3f29d Antony Chazapis
        def fn(func):
99 a9b3f29d Antony Chazapis
            return backend_method(func, autocommit)
100 a9b3f29d Antony Chazapis
        return fn
101 a9b3f29d Antony Chazapis
102 a9b3f29d Antony Chazapis
    if not autocommit:
103 a9b3f29d Antony Chazapis
        return func
104 a9b3f29d Antony Chazapis
    def fn(self, *args, **kw):
105 2c5363a0 Antony Chazapis
        self.wrapper.execute()
106 a9b3f29d Antony Chazapis
        try:
107 39ef6f41 Antony Chazapis
            self.messages = []
108 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
109 39ef6f41 Antony Chazapis
            for m in self.messages:
110 39ef6f41 Antony Chazapis
                self.queue.send(*m)
111 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
112 a9b3f29d Antony Chazapis
            return ret
113 a9b3f29d Antony Chazapis
        except:
114 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
115 a9b3f29d Antony Chazapis
            raise
116 a9b3f29d Antony Chazapis
    return fn
117 a9b3f29d Antony Chazapis
118 a9b3f29d Antony Chazapis
119 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
120 a9b3f29d Antony Chazapis
    """A modular backend.
121 a9b3f29d Antony Chazapis
    
122 e9363f82 Antony Chazapis
    Uses modules for SQL functions and storage.
123 a9b3f29d Antony Chazapis
    """
124 a9b3f29d Antony Chazapis
    
125 46286f5f Antony Chazapis
    def __init__(self, db_module=None, db_connection=None,
126 f3b65e8f Antony Chazapis
                 block_module=None, block_path=None, block_umask=None,
127 46286f5f Antony Chazapis
                 queue_module=None, queue_connection=None):
128 228de81b Antony Chazapis
        db_module = db_module or DEFAULT_DB_MODULE
129 228de81b Antony Chazapis
        db_connection = db_connection or DEFAULT_DB_CONNECTION
130 228de81b Antony Chazapis
        block_module = block_module or DEFAULT_BLOCK_MODULE
131 228de81b Antony Chazapis
        block_path = block_path or DEFAULT_BLOCK_PATH
132 f3b65e8f Antony Chazapis
        block_umask = block_umask or DEFAULT_BLOCK_UMASK
133 46286f5f Antony Chazapis
        #queue_module = queue_module or DEFAULT_QUEUE_MODULE
134 46286f5f Antony Chazapis
        #queue_connection = queue_connection or DEFAULT_QUEUE_CONNECTION
135 f81e20b0 Giorgos Verigakis
        
136 a9b3f29d Antony Chazapis
        self.hash_algorithm = 'sha256'
137 a9b3f29d Antony Chazapis
        self.block_size = 4 * 1024 * 1024 # 4MB
138 a9b3f29d Antony Chazapis
        
139 228de81b Antony Chazapis
        self.default_policy = {'quota': DEFAULT_QUOTA, 'versioning': DEFAULT_VERSIONING}
140 a9b3f29d Antony Chazapis
        
141 46286f5f Antony Chazapis
        def load_module(m):
142 46286f5f Antony Chazapis
            __import__(m)
143 46286f5f Antony Chazapis
            return sys.modules[m]
144 a9b3f29d Antony Chazapis
        
145 46286f5f Antony Chazapis
        self.db_module = load_module(db_module)
146 46286f5f Antony Chazapis
        self.wrapper = self.db_module.DBWrapper(db_connection)
147 e9363f82 Antony Chazapis
        params = {'wrapper': self.wrapper}
148 e9363f82 Antony Chazapis
        self.permissions = self.db_module.Permissions(**params)
149 e9363f82 Antony Chazapis
        for x in ['READ', 'WRITE']:
150 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
151 e9363f82 Antony Chazapis
        self.node = self.db_module.Node(**params)
152 33b4e4a6 Antony Chazapis
        for x in ['ROOTNODE', 'SERIAL', 'HASH', 'SIZE', 'TYPE', 'MTIME', 'MUSER', 'UUID', 'CHECKSUM', 'CLUSTER', 'MATCH_PREFIX', 'MATCH_EXACT']:
153 e9363f82 Antony Chazapis
            setattr(self, x, getattr(self.db_module, x))
154 e9363f82 Antony Chazapis
        
155 46286f5f Antony Chazapis
        self.block_module = load_module(block_module)
156 7ca7bb08 Antony Chazapis
        params = {'path': block_path,
157 7ca7bb08 Antony Chazapis
                  'block_size': self.block_size,
158 f3b65e8f Antony Chazapis
                  'hash_algorithm': self.hash_algorithm,
159 f3b65e8f Antony Chazapis
                  'umask': block_umask}
160 7ca7bb08 Antony Chazapis
        self.store = self.block_module.Store(**params)
161 46286f5f Antony Chazapis
162 46286f5f Antony Chazapis
        if queue_module and queue_connection:
163 46286f5f Antony Chazapis
            self.queue_module = load_module(queue_module)
164 fa9cae7e Antony Chazapis
            params = {'exchange': queue_connection,
165 fa9cae7e Antony Chazapis
                      'client_id': QUEUE_CLIENT_ID}
166 46286f5f Antony Chazapis
            self.queue = self.queue_module.Queue(**params)
167 46286f5f Antony Chazapis
        else:
168 46286f5f Antony Chazapis
            class NoQueue:
169 1a239dc1 Sofia Papagiannaki
                def send(self, *args):
170 46286f5f Antony Chazapis
                    pass
171 b9a8feec root
                
172 b9a8feec root
                def close(self):
173 b9a8feec root
                    pass
174 46286f5f Antony Chazapis
            
175 46286f5f Antony Chazapis
            self.queue = NoQueue()
176 a9b3f29d Antony Chazapis
    
177 d14fe290 Antony Chazapis
    def close(self):
178 d14fe290 Antony Chazapis
        self.wrapper.close()
179 b9a8feec root
        self.queue.close()
180 d14fe290 Antony Chazapis
    
181 a9b3f29d Antony Chazapis
    @backend_method
182 a9b3f29d Antony Chazapis
    def list_accounts(self, user, marker=None, limit=10000):
183 a9b3f29d Antony Chazapis
        """Return a list of accounts the user can access."""
184 a9b3f29d Antony Chazapis
        
185 02c4d2ba Antony Chazapis
        logger.debug("list_accounts: %s %s %s", user, marker, limit)
186 a9b3f29d Antony Chazapis
        allowed = self._allowed_accounts(user)
187 a9b3f29d Antony Chazapis
        start, limit = self._list_limits(allowed, marker, limit)
188 a9b3f29d Antony Chazapis
        return allowed[start:start + limit]
189 a9b3f29d Antony Chazapis
    
190 a9b3f29d Antony Chazapis
    @backend_method
191 82482e2c Antony Chazapis
    def get_account_meta(self, user, account, domain, until=None, include_user_defined=True):
192 cb69c154 Antony Chazapis
        """Return a dictionary with the account metadata for the domain."""
193 a9b3f29d Antony Chazapis
        
194 fe2db49d Sofia Papagiannaki
        logger.debug("get_account_meta: %s %s %s %s", user, account, domain, until)
195 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, user == account)
196 a9b3f29d Antony Chazapis
        if user != account:
197 44ad5860 Antony Chazapis
            if until or node is None or account not in self._allowed_accounts(user):
198 a9b3f29d Antony Chazapis
                raise NotAllowedError
199 a9b3f29d Antony Chazapis
        try:
200 44ad5860 Antony Chazapis
            props = self._get_properties(node, until)
201 2c5363a0 Antony Chazapis
            mtime = props[self.MTIME]
202 a9b3f29d Antony Chazapis
        except NameError:
203 62f915a1 Antony Chazapis
            props = None
204 a9b3f29d Antony Chazapis
            mtime = until
205 62f915a1 Antony Chazapis
        count, bytes, tstamp = self._get_statistics(node, until)
206 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
207 a9b3f29d Antony Chazapis
        if until is None:
208 a9b3f29d Antony Chazapis
            modified = tstamp
209 a9b3f29d Antony Chazapis
        else:
210 62f915a1 Antony Chazapis
            modified = self._get_statistics(node)[2] # Overall last modification.
211 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
212 a9b3f29d Antony Chazapis
        
213 a9b3f29d Antony Chazapis
        if user != account:
214 a9b3f29d Antony Chazapis
            meta = {'name': account}
215 a9b3f29d Antony Chazapis
        else:
216 44ad5860 Antony Chazapis
            meta = {}
217 82482e2c Antony Chazapis
            if props is not None and include_user_defined:
218 4819d34f Antony Chazapis
                meta.update(dict(self.node.attribute_get(props[self.SERIAL], domain)))
219 a9b3f29d Antony Chazapis
            if until is not None:
220 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
221 44ad5860 Antony Chazapis
            meta.update({'name': account, 'count': count, 'bytes': bytes})
222 a9b3f29d Antony Chazapis
        meta.update({'modified': modified})
223 a9b3f29d Antony Chazapis
        return meta
224 a9b3f29d Antony Chazapis
    
225 a9b3f29d Antony Chazapis
    @backend_method
226 cb69c154 Antony Chazapis
    def update_account_meta(self, user, account, domain, meta, replace=False):
227 cb69c154 Antony Chazapis
        """Update the metadata associated with the account for the domain."""
228 a9b3f29d Antony Chazapis
        
229 fe2db49d Sofia Papagiannaki
        logger.debug("update_account_meta: %s %s %s %s %s", user, account, domain, meta, replace)
230 a9b3f29d Antony Chazapis
        if user != account:
231 a9b3f29d Antony Chazapis
            raise NotAllowedError
232 c915d3bf Antony Chazapis
        path, node = self._lookup_account(account, True)
233 4819d34f Antony Chazapis
        self._put_metadata(user, node, domain, meta, replace)
234 a9b3f29d Antony Chazapis
    
235 a9b3f29d Antony Chazapis
    @backend_method
236 a9b3f29d Antony Chazapis
    def get_account_groups(self, user, account):
237 a9b3f29d Antony Chazapis
        """Return a dictionary with the user groups defined for this account."""
238 a9b3f29d Antony Chazapis
        
239 fe2db49d Sofia Papagiannaki
        logger.debug("get_account_groups: %s %s", user, account)
240 a9b3f29d Antony Chazapis
        if user != account:
241 a9b3f29d Antony Chazapis
            if account not in self._allowed_accounts(user):
242 a9b3f29d Antony Chazapis
                raise NotAllowedError
243 a9b3f29d Antony Chazapis
            return {}
244 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
245 0f9d752c Antony Chazapis
        return self.permissions.group_dict(account)
246 a9b3f29d Antony Chazapis
    
247 a9b3f29d Antony Chazapis
    @backend_method
248 a9b3f29d Antony Chazapis
    def update_account_groups(self, user, account, groups, replace=False):
249 a9b3f29d Antony Chazapis
        """Update the groups associated with the account."""
250 a9b3f29d Antony Chazapis
        
251 fe2db49d Sofia Papagiannaki
        logger.debug("update_account_groups: %s %s %s %s", user, account, groups, replace)
252 a9b3f29d Antony Chazapis
        if user != account:
253 a9b3f29d Antony Chazapis
            raise NotAllowedError
254 44ad5860 Antony Chazapis
        self._lookup_account(account, True)
255 a9b3f29d Antony Chazapis
        self._check_groups(groups)
256 0f9d752c Antony Chazapis
        if replace:
257 0f9d752c Antony Chazapis
            self.permissions.group_destroy(account)
258 0f9d752c Antony Chazapis
        for k, v in groups.iteritems():
259 0f9d752c Antony Chazapis
            if not replace: # If not already deleted.
260 0f9d752c Antony Chazapis
                self.permissions.group_delete(account, k)
261 0f9d752c Antony Chazapis
            if v:
262 0f9d752c Antony Chazapis
                self.permissions.group_addmany(account, k, v)
263 a9b3f29d Antony Chazapis
    
264 a9b3f29d Antony Chazapis
    @backend_method
265 b2832c6a Antony Chazapis
    def get_account_policy(self, user, account):
266 b2832c6a Antony Chazapis
        """Return a dictionary with the account policy."""
267 b2832c6a Antony Chazapis
        
268 fe2db49d Sofia Papagiannaki
        logger.debug("get_account_policy: %s %s", user, account)
269 b2832c6a Antony Chazapis
        if user != account:
270 647a5f48 Antony Chazapis
            if account not in self._allowed_accounts(user):
271 647a5f48 Antony Chazapis
                raise NotAllowedError
272 647a5f48 Antony Chazapis
            return {}
273 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
274 b9064632 Antony Chazapis
        return self._get_policy(node)
275 b2832c6a Antony Chazapis
    
276 b2832c6a Antony Chazapis
    @backend_method
277 b2832c6a Antony Chazapis
    def update_account_policy(self, user, account, policy, replace=False):
278 b2832c6a Antony Chazapis
        """Update the policy associated with the account."""
279 b2832c6a Antony Chazapis
        
280 fe2db49d Sofia Papagiannaki
        logger.debug("update_account_policy: %s %s %s %s", user, account, policy, replace)
281 b2832c6a Antony Chazapis
        if user != account:
282 b2832c6a Antony Chazapis
            raise NotAllowedError
283 b2832c6a Antony Chazapis
        path, node = self._lookup_account(account, True)
284 b2832c6a Antony Chazapis
        self._check_policy(policy)
285 b2832c6a Antony Chazapis
        self._put_policy(node, policy, replace)
286 b2832c6a Antony Chazapis
    
287 b2832c6a Antony Chazapis
    @backend_method
288 8693b873 Sofia Papagiannaki
    def put_account(self, user, account, policy={}):
289 a9b3f29d Antony Chazapis
        """Create a new account with the given name."""
290 a9b3f29d Antony Chazapis
        
291 fe2db49d Sofia Papagiannaki
        logger.debug("put_account: %s %s %s", user, account, policy)
292 a9b3f29d Antony Chazapis
        if user != account:
293 a9b3f29d Antony Chazapis
            raise NotAllowedError
294 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
295 44ad5860 Antony Chazapis
        if node is not None:
296 7efc9f86 Sofia Papagiannaki
            raise AccountExists('Account already exists')
297 b2832c6a Antony Chazapis
        if policy:
298 b2832c6a Antony Chazapis
            self._check_policy(policy)
299 b2832c6a Antony Chazapis
        node = self._put_path(user, self.ROOTNODE, account)
300 b2832c6a Antony Chazapis
        self._put_policy(node, policy, True)
301 a9b3f29d Antony Chazapis
    
302 a9b3f29d Antony Chazapis
    @backend_method
303 a9b3f29d Antony Chazapis
    def delete_account(self, user, account):
304 a9b3f29d Antony Chazapis
        """Delete the account with the given name."""
305 a9b3f29d Antony Chazapis
        
306 fe2db49d Sofia Papagiannaki
        logger.debug("delete_account: %s %s", user, account)
307 a9b3f29d Antony Chazapis
        if user != account:
308 a9b3f29d Antony Chazapis
            raise NotAllowedError
309 c915d3bf Antony Chazapis
        node = self.node.node_lookup(account)
310 c915d3bf Antony Chazapis
        if node is None:
311 c915d3bf Antony Chazapis
            return
312 c915d3bf Antony Chazapis
        if not self.node.node_remove(node):
313 7efc9f86 Sofia Papagiannaki
            raise AccountNotEmpty('Account is not empty')
314 0f9d752c Antony Chazapis
        self.permissions.group_destroy(account)
315 a9b3f29d Antony Chazapis
    
316 62f915a1 Antony Chazapis
    @backend_method
317 90ee1eb3 Sofia Papagiannaki
    def list_containers(self, user, account, marker=None, limit=10000, shared=False, until=None, public=False):
318 62f915a1 Antony Chazapis
        """Return a list of containers existing under an account."""
319 62f915a1 Antony Chazapis
        
320 fe2db49d Sofia Papagiannaki
        logger.debug("list_containers: %s %s %s %s %s %s %s", user, account, marker, limit, shared, until, public)
321 62f915a1 Antony Chazapis
        if user != account:
322 62f915a1 Antony Chazapis
            if until or account not in self._allowed_accounts(user):
323 62f915a1 Antony Chazapis
                raise NotAllowedError
324 62f915a1 Antony Chazapis
            allowed = self._allowed_containers(user, account)
325 62f915a1 Antony Chazapis
            start, limit = self._list_limits(allowed, marker, limit)
326 62f915a1 Antony Chazapis
            return allowed[start:start + limit]
327 90ee1eb3 Sofia Papagiannaki
        if shared or public:
328 56ac7c81 Sofia Papagiannaki
            allowed = set()
329 90ee1eb3 Sofia Papagiannaki
            if shared:
330 56ac7c81 Sofia Papagiannaki
                allowed.update([x.split('/', 2)[1] for x in self.permissions.access_list_shared(account)])
331 90ee1eb3 Sofia Papagiannaki
            if public:
332 56ac7c81 Sofia Papagiannaki
                allowed.update([x[0].split('/', 2)[1] for x in self.permissions.public_list(account)])
333 56ac7c81 Sofia Papagiannaki
            allowed = sorted(allowed)
334 62f915a1 Antony Chazapis
            start, limit = self._list_limits(allowed, marker, limit)
335 62f915a1 Antony Chazapis
            return allowed[start:start + limit]
336 62f915a1 Antony Chazapis
        node = self.node.node_lookup(account)
337 cf4a7a7b Sofia Papagiannaki
        containers = [x[0] for x in self._list_object_properties(node, account, '', '/', marker, limit, False, None, [], until)]
338 cf4a7a7b Sofia Papagiannaki
        start, limit = self._list_limits([x[0] for x in containers], marker, limit)
339 cf4a7a7b Sofia Papagiannaki
        return containers[start:start + limit]
340 371d907a Antony Chazapis
    
341 371d907a Antony Chazapis
    @backend_method
342 371d907a Antony Chazapis
    def list_container_meta(self, user, account, container, domain, until=None):
343 371d907a Antony Chazapis
        """Return a list with all the container's object meta keys for the domain."""
344 371d907a Antony Chazapis
        
345 fe2db49d Sofia Papagiannaki
        logger.debug("list_container_meta: %s %s %s %s %s", user, account, container, domain, until)
346 371d907a Antony Chazapis
        allowed = []
347 371d907a Antony Chazapis
        if user != account:
348 371d907a Antony Chazapis
            if until:
349 371d907a Antony Chazapis
                raise NotAllowedError
350 371d907a Antony Chazapis
            allowed = self.permissions.access_list_paths(user, '/'.join((account, container)))
351 371d907a Antony Chazapis
            if not allowed:
352 371d907a Antony Chazapis
                raise NotAllowedError
353 371d907a Antony Chazapis
        path, node = self._lookup_container(account, container)
354 371d907a Antony Chazapis
        before = until if until is not None else inf
355 371d907a Antony Chazapis
        allowed = self._get_formatted_paths(allowed)
356 371d907a Antony Chazapis
        return self.node.latest_attribute_keys(node, domain, before, CLUSTER_DELETED, allowed)
357 a9b3f29d Antony Chazapis
    
358 a9b3f29d Antony Chazapis
    @backend_method
359 82482e2c Antony Chazapis
    def get_container_meta(self, user, account, container, domain, until=None, include_user_defined=True):
360 cb69c154 Antony Chazapis
        """Return a dictionary with the container metadata for the domain."""
361 a9b3f29d Antony Chazapis
        
362 fe2db49d Sofia Papagiannaki
        logger.debug("get_container_meta: %s %s %s %s %s", user, account, container, domain, until)
363 a9b3f29d Antony Chazapis
        if user != account:
364 a9b3f29d Antony Chazapis
            if until or container not in self._allowed_containers(user, account):
365 a9b3f29d Antony Chazapis
                raise NotAllowedError
366 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
367 c915d3bf Antony Chazapis
        props = self._get_properties(node, until)
368 2c5363a0 Antony Chazapis
        mtime = props[self.MTIME]
369 62f915a1 Antony Chazapis
        count, bytes, tstamp = self._get_statistics(node, until)
370 62f915a1 Antony Chazapis
        tstamp = max(tstamp, mtime)
371 a9b3f29d Antony Chazapis
        if until is None:
372 a9b3f29d Antony Chazapis
            modified = tstamp
373 a9b3f29d Antony Chazapis
        else:
374 62f915a1 Antony Chazapis
            modified = self._get_statistics(node)[2] # Overall last modification.
375 62f915a1 Antony Chazapis
            modified = max(modified, mtime)
376 a9b3f29d Antony Chazapis
        
377 a9b3f29d Antony Chazapis
        if user != account:
378 c915d3bf Antony Chazapis
            meta = {'name': container}
379 a9b3f29d Antony Chazapis
        else:
380 82482e2c Antony Chazapis
            meta = {}
381 82482e2c Antony Chazapis
            if include_user_defined:
382 82482e2c Antony Chazapis
                meta.update(dict(self.node.attribute_get(props[self.SERIAL], domain)))
383 a9b3f29d Antony Chazapis
            if until is not None:
384 a9b3f29d Antony Chazapis
                meta.update({'until_timestamp': tstamp})
385 c915d3bf Antony Chazapis
            meta.update({'name': container, 'count': count, 'bytes': bytes})
386 c915d3bf Antony Chazapis
        meta.update({'modified': modified})
387 a9b3f29d Antony Chazapis
        return meta
388 a9b3f29d Antony Chazapis
    
389 a9b3f29d Antony Chazapis
    @backend_method
390 cb69c154 Antony Chazapis
    def update_container_meta(self, user, account, container, domain, meta, replace=False):
391 cb69c154 Antony Chazapis
        """Update the metadata associated with the container for the domain."""
392 a9b3f29d Antony Chazapis
        
393 fe2db49d Sofia Papagiannaki
        logger.debug("update_container_meta: %s %s %s %s %s %s", user, account, container, domain, meta, replace)
394 a9b3f29d Antony Chazapis
        if user != account:
395 a9b3f29d Antony Chazapis
            raise NotAllowedError
396 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
397 f9ea264b Antony Chazapis
        src_version_id, dest_version_id = self._put_metadata(user, node, domain, meta, replace)
398 f9ea264b Antony Chazapis
        if src_version_id is not None:
399 f9ea264b Antony Chazapis
            versioning = self._get_policy(node)['versioning']
400 f9ea264b Antony Chazapis
            if versioning != 'auto':
401 f9ea264b Antony Chazapis
                self.node.version_remove(src_version_id)
402 a9b3f29d Antony Chazapis
    
403 a9b3f29d Antony Chazapis
    @backend_method
404 a9b3f29d Antony Chazapis
    def get_container_policy(self, user, account, container):
405 a9b3f29d Antony Chazapis
        """Return a dictionary with the container policy."""
406 a9b3f29d Antony Chazapis
        
407 fe2db49d Sofia Papagiannaki
        logger.debug("get_container_policy: %s %s %s", user, account, container)
408 a9b3f29d Antony Chazapis
        if user != account:
409 a9b3f29d Antony Chazapis
            if container not in self._allowed_containers(user, account):
410 a9b3f29d Antony Chazapis
                raise NotAllowedError
411 a9b3f29d Antony Chazapis
            return {}
412 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
413 b9064632 Antony Chazapis
        return self._get_policy(node)
414 a9b3f29d Antony Chazapis
    
415 a9b3f29d Antony Chazapis
    @backend_method
416 a9b3f29d Antony Chazapis
    def update_container_policy(self, user, account, container, policy, replace=False):
417 b2832c6a Antony Chazapis
        """Update the policy associated with the container."""
418 a9b3f29d Antony Chazapis
        
419 fe2db49d Sofia Papagiannaki
        logger.debug("update_container_policy: %s %s %s %s %s", user, account, container, policy, replace)
420 a9b3f29d Antony Chazapis
        if user != account:
421 a9b3f29d Antony Chazapis
            raise NotAllowedError
422 5e7485da Antony Chazapis
        path, node = self._lookup_container(account, container)
423 a9b3f29d Antony Chazapis
        self._check_policy(policy)
424 b2832c6a Antony Chazapis
        self._put_policy(node, policy, replace)
425 a9b3f29d Antony Chazapis
    
426 a9b3f29d Antony Chazapis
    @backend_method
427 8693b873 Sofia Papagiannaki
    def put_container(self, user, account, container, policy={}):
428 a9b3f29d Antony Chazapis
        """Create a new container with the given name."""
429 a9b3f29d Antony Chazapis
        
430 fe2db49d Sofia Papagiannaki
        logger.debug("put_container: %s %s %s %s", user, account, container, policy)
431 a9b3f29d Antony Chazapis
        if user != account:
432 a9b3f29d Antony Chazapis
            raise NotAllowedError
433 a9b3f29d Antony Chazapis
        try:
434 c915d3bf Antony Chazapis
            path, node = self._lookup_container(account, container)
435 a9b3f29d Antony Chazapis
        except NameError:
436 a9b3f29d Antony Chazapis
            pass
437 a9b3f29d Antony Chazapis
        else:
438 7efc9f86 Sofia Papagiannaki
            raise ContainerExists('Container already exists')
439 a9b3f29d Antony Chazapis
        if policy:
440 a9b3f29d Antony Chazapis
            self._check_policy(policy)
441 a9b3f29d Antony Chazapis
        path = '/'.join((account, container))
442 5e7485da Antony Chazapis
        node = self._put_path(user, self._lookup_account(account, True)[1], path)
443 b2832c6a Antony Chazapis
        self._put_policy(node, policy, True)
444 a9b3f29d Antony Chazapis
    
445 a9b3f29d Antony Chazapis
    @backend_method
446 fe2db49d Sofia Papagiannaki
    def delete_container(self, user, account, container, until=None, prefix='', delimiter=None):
447 a9b3f29d Antony Chazapis
        """Delete/purge the container with the given name."""
448 a9b3f29d Antony Chazapis
        
449 fe2db49d Sofia Papagiannaki
        logger.debug("delete_container: %s %s %s %s %s %s", user, account, container, until, prefix, delimiter)
450 a9b3f29d Antony Chazapis
        if user != account:
451 a9b3f29d Antony Chazapis
            raise NotAllowedError
452 c915d3bf Antony Chazapis
        path, node = self._lookup_container(account, container)
453 a9b3f29d Antony Chazapis
        
454 a9b3f29d Antony Chazapis
        if until is not None:
455 813e42e5 Antony Chazapis
            hashes, size = self.node.node_purge_children(node, until, CLUSTER_HISTORY)
456 04230536 Antony Chazapis
            for h in hashes:
457 04230536 Antony Chazapis
                self.store.map_delete(h)
458 c915d3bf Antony Chazapis
            self.node.node_purge_children(node, until, CLUSTER_DELETED)
459 b82d3277 Sofia Papagiannaki
            self._report_size_change(user, account, -size, {'action': 'container purge', 'path':path})
460 a9b3f29d Antony Chazapis
            return
461 a9b3f29d Antony Chazapis
        
462 e46b2bcf Sofia Papagiannaki
        if not delimiter:
463 e46b2bcf Sofia Papagiannaki
            if self._get_statistics(node)[0] > 0:
464 e46b2bcf Sofia Papagiannaki
                raise ContainerNotEmpty('Container is not empty')
465 e46b2bcf Sofia Papagiannaki
            hashes, size = self.node.node_purge_children(node, inf, CLUSTER_HISTORY)
466 e46b2bcf Sofia Papagiannaki
            for h in hashes:
467 e46b2bcf Sofia Papagiannaki
                self.store.map_delete(h)
468 e46b2bcf Sofia Papagiannaki
            self.node.node_purge_children(node, inf, CLUSTER_DELETED)
469 e46b2bcf Sofia Papagiannaki
            self.node.node_remove(node)
470 b82d3277 Sofia Papagiannaki
            self._report_size_change(user, account, -size, {'action': 'container delete', 'path':path})
471 e46b2bcf Sofia Papagiannaki
        else:
472 e46b2bcf Sofia Papagiannaki
                # remove only contents
473 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)
474 e46b2bcf Sofia Papagiannaki
            paths = []
475 e46b2bcf Sofia Papagiannaki
            for t in src_names:
476 e46b2bcf Sofia Papagiannaki
                path = '/'.join((account, container, t[0]))
477 e46b2bcf Sofia Papagiannaki
                node = t[2]
478 e46b2bcf Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(user, node, size=0, type='', hash=None, checksum='', cluster=CLUSTER_DELETED)
479 e46b2bcf Sofia Papagiannaki
                del_size = self._apply_versioning(account, container, src_version_id)
480 e46b2bcf Sofia Papagiannaki
                if del_size:
481 b82d3277 Sofia Papagiannaki
                    self._report_size_change(user, account, -del_size, {'action': 'object delete', 'path':path})
482 e46b2bcf Sofia Papagiannaki
                self._report_object_change(user, account, path, details={'action': 'object delete'})
483 e46b2bcf Sofia Papagiannaki
                paths.append(path)
484 e46b2bcf Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
485 a9b3f29d Antony Chazapis
    
486 90ee1eb3 Sofia Papagiannaki
    def _list_objects(self, user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, all_props, public):
487 15a96c3e Antony Chazapis
        if user != account and until:
488 15a96c3e Antony Chazapis
            raise NotAllowedError
489 cf4a7a7b Sofia Papagiannaki
        if shared and public:
490 cf4a7a7b Sofia Papagiannaki
            # get shared first
491 cf4a7a7b Sofia Papagiannaki
            shared = self._list_object_permissions(user, account, container, prefix, shared=True, public=False)
492 31e1acd3 Sofia Papagiannaki
            objects = set()
493 cf4a7a7b Sofia Papagiannaki
            if shared:
494 cf4a7a7b Sofia Papagiannaki
                path, node = self._lookup_container(account, container)
495 cf4a7a7b Sofia Papagiannaki
                shared = self._get_formatted_paths(shared)
496 31e1acd3 Sofia Papagiannaki
                objects |= set(self._list_object_properties(node, path, prefix, delimiter, marker, limit, virtual, domain, keys, until, size_range, shared, all_props))
497 cf4a7a7b Sofia Papagiannaki
            
498 cf4a7a7b Sofia Papagiannaki
            # get public
499 31e1acd3 Sofia Papagiannaki
            objects |= set(self._list_public_object_properties(user, account, container, prefix, all_props))
500 31e1acd3 Sofia Papagiannaki
            objects = list(objects)
501 cf4a7a7b Sofia Papagiannaki
            
502 cf4a7a7b Sofia Papagiannaki
            objects.sort(key=lambda x: x[0])
503 cf4a7a7b Sofia Papagiannaki
            start, limit = self._list_limits([x[0] for x in objects], marker, limit)
504 cf4a7a7b Sofia Papagiannaki
            return objects[start:start + limit]
505 cf4a7a7b Sofia Papagiannaki
        elif public:
506 cf4a7a7b Sofia Papagiannaki
            objects = self._list_public_object_properties(user, account, container, prefix, all_props)
507 cf4a7a7b Sofia Papagiannaki
            start, limit = self._list_limits([x[0] for x in objects], marker, limit)
508 cf4a7a7b Sofia Papagiannaki
            return objects[start:start + limit]
509 cf4a7a7b Sofia Papagiannaki
        
510 90ee1eb3 Sofia Papagiannaki
        allowed = self._list_object_permissions(user, account, container, prefix, shared, public)
511 cf4a7a7b Sofia Papagiannaki
        if shared and not allowed:
512 fcd37c40 Antony Chazapis
            return []
513 15a96c3e Antony Chazapis
        path, node = self._lookup_container(account, container)
514 15a96c3e Antony Chazapis
        allowed = self._get_formatted_paths(allowed)
515 cf4a7a7b Sofia Papagiannaki
        objects = self._list_object_properties(node, path, prefix, delimiter, marker, limit, virtual, domain, keys, until, size_range, allowed, all_props)
516 cf4a7a7b Sofia Papagiannaki
        start, limit = self._list_limits([x[0] for x in objects], marker, limit)
517 cf4a7a7b Sofia Papagiannaki
        return objects[start:start + limit]
518 15a96c3e Antony Chazapis
    
519 cf4a7a7b Sofia Papagiannaki
    def _list_public_object_properties(self, user, account, container, prefix, all_props):
520 cf4a7a7b Sofia Papagiannaki
        public = self._list_object_permissions(user, account, container, prefix, shared=False, public=True)
521 cf4a7a7b Sofia Papagiannaki
        paths, nodes = self._lookup_objects(public)
522 cf4a7a7b Sofia Papagiannaki
        path = '/'.join((account, container))
523 cf4a7a7b Sofia Papagiannaki
        cont_prefix = path + '/'
524 cf4a7a7b Sofia Papagiannaki
        paths = [x[len(cont_prefix):] for x in paths]
525 cf4a7a7b Sofia Papagiannaki
        props = self.node.version_lookup_bulk(nodes, all_props=all_props)
526 cf4a7a7b Sofia Papagiannaki
        objects = [(path,) + props for path, props in zip(paths, props)]
527 cf4a7a7b Sofia Papagiannaki
        return objects
528 cf4a7a7b Sofia Papagiannaki
        
529 4d15c94e Sofia Papagiannaki
    def _list_objects_no_limit(self, user, account, container, prefix, delimiter, virtual, domain, keys, shared, until, size_range, all_props, public):
530 4d15c94e Sofia Papagiannaki
        objects = []
531 4d15c94e Sofia Papagiannaki
        while True:
532 4d15c94e Sofia Papagiannaki
            marker = objects[-1] if objects else None
533 4d15c94e Sofia Papagiannaki
            limit = 10000
534 4d15c94e Sofia Papagiannaki
            l = self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, all_props, public)
535 4d15c94e Sofia Papagiannaki
            objects.extend(l)
536 4d15c94e Sofia Papagiannaki
            if not l or len(l) < limit:
537 4d15c94e Sofia Papagiannaki
                break
538 4d15c94e Sofia Papagiannaki
        return objects
539 4d15c94e Sofia Papagiannaki
    
540 90ee1eb3 Sofia Papagiannaki
    def _list_object_permissions(self, user, account, container, prefix, shared, public):
541 62f915a1 Antony Chazapis
        allowed = []
542 fcd37c40 Antony Chazapis
        path = '/'.join((account, container, prefix)).rstrip('/')
543 62f915a1 Antony Chazapis
        if user != account:
544 fcd37c40 Antony Chazapis
            allowed = self.permissions.access_list_paths(user, path)
545 62f915a1 Antony Chazapis
            if not allowed:
546 62f915a1 Antony Chazapis
                raise NotAllowedError
547 62f915a1 Antony Chazapis
        else:
548 56ac7c81 Sofia Papagiannaki
            allowed = set()
549 62f915a1 Antony Chazapis
            if shared:
550 56ac7c81 Sofia Papagiannaki
                allowed.update(self.permissions.access_list_shared(path))
551 c53c4def Sofia Papagiannaki
            if public:
552 56ac7c81 Sofia Papagiannaki
                allowed.update([x[0] for x in self.permissions.public_list(path)])
553 56ac7c81 Sofia Papagiannaki
            allowed = sorted(allowed)
554 c53c4def Sofia Papagiannaki
            if not allowed:
555 c53c4def Sofia Papagiannaki
                return []
556 15a96c3e Antony Chazapis
        return allowed
557 62f915a1 Antony Chazapis
    
558 62f915a1 Antony Chazapis
    @backend_method
559 90ee1eb3 Sofia Papagiannaki
    def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None, size_range=None, public=False):
560 371d907a Antony Chazapis
        """Return a list of object (name, version_id) tuples existing under a container."""
561 62f915a1 Antony Chazapis
        
562 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)
563 90ee1eb3 Sofia Papagiannaki
        return self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, False, public)
564 371d907a Antony Chazapis
    
565 371d907a Antony Chazapis
    @backend_method
566 90ee1eb3 Sofia Papagiannaki
    def list_object_meta(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], shared=False, until=None, size_range=None, public=False):
567 371d907a Antony Chazapis
        """Return a list of object metadata dicts existing under a container."""
568 371d907a Antony Chazapis
        
569 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)
570 90ee1eb3 Sofia Papagiannaki
        props = self._list_objects(user, account, container, prefix, delimiter, marker, limit, virtual, domain, keys, shared, until, size_range, True, public)
571 371d907a Antony Chazapis
        objects = []
572 371d907a Antony Chazapis
        for p in props:
573 371d907a Antony Chazapis
            if len(p) == 2:
574 371d907a Antony Chazapis
                objects.append({'subdir': p[0]})
575 371d907a Antony Chazapis
            else:
576 371d907a Antony Chazapis
                objects.append({'name': p[0],
577 371d907a Antony Chazapis
                                'bytes': p[self.SIZE + 1],
578 371d907a Antony Chazapis
                                'type': p[self.TYPE + 1],
579 371d907a Antony Chazapis
                                'hash': p[self.HASH + 1],
580 371d907a Antony Chazapis
                                'version': p[self.SERIAL + 1],
581 371d907a Antony Chazapis
                                'version_timestamp': p[self.MTIME + 1],
582 371d907a Antony Chazapis
                                'modified': p[self.MTIME + 1] if until is None else None,
583 371d907a Antony Chazapis
                                'modified_by': p[self.MUSER + 1],
584 371d907a Antony Chazapis
                                'uuid': p[self.UUID + 1],
585 371d907a Antony Chazapis
                                'checksum': p[self.CHECKSUM + 1]})
586 371d907a Antony Chazapis
        return objects
587 a9b3f29d Antony Chazapis
    
588 a9b3f29d Antony Chazapis
    @backend_method
589 15a96c3e Antony Chazapis
    def list_object_permissions(self, user, account, container, prefix=''):
590 15a96c3e Antony Chazapis
        """Return a list of paths that enforce permissions under a container."""
591 15a96c3e Antony Chazapis
        
592 fe2db49d Sofia Papagiannaki
        logger.debug("list_object_permissions: %s %s %s %s", user, account, container, prefix)
593 90ee1eb3 Sofia Papagiannaki
        return self._list_object_permissions(user, account, container, prefix, True, False)
594 15a96c3e Antony Chazapis
    
595 15a96c3e Antony Chazapis
    @backend_method
596 15a96c3e Antony Chazapis
    def list_object_public(self, user, account, container, prefix=''):
597 15a96c3e Antony Chazapis
        """Return a dict mapping paths to public ids for objects that are public under a container."""
598 15a96c3e Antony Chazapis
        
599 fe2db49d Sofia Papagiannaki
        logger.debug("list_object_public: %s %s %s %s", user, account, container, prefix)
600 15a96c3e Antony Chazapis
        public = {}
601 15a96c3e Antony Chazapis
        for path, p in self.permissions.public_list('/'.join((account, container, prefix))):
602 15a96c3e Antony Chazapis
            public[path] = p + ULTIMATE_ANSWER
603 15a96c3e Antony Chazapis
        return public
604 15a96c3e Antony Chazapis
    
605 15a96c3e Antony Chazapis
    @backend_method
606 82482e2c Antony Chazapis
    def get_object_meta(self, user, account, container, name, domain, version=None, include_user_defined=True):
607 cb69c154 Antony Chazapis
        """Return a dictionary with the object metadata for the domain."""
608 a9b3f29d Antony Chazapis
        
609 fe2db49d Sofia Papagiannaki
        logger.debug("get_object_meta: %s %s %s %s %s %s", user, account, container, name, domain, version)
610 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
611 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
612 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
613 a9b3f29d Antony Chazapis
        if version is None:
614 2c5363a0 Antony Chazapis
            modified = props[self.MTIME]
615 a9b3f29d Antony Chazapis
        else:
616 97d45f69 Antony Chazapis
            try:
617 97d45f69 Antony Chazapis
                modified = self._get_version(node)[self.MTIME] # Overall last modification.
618 97d45f69 Antony Chazapis
            except NameError: # Object may be deleted.
619 97d45f69 Antony Chazapis
                del_props = self.node.version_lookup(node, inf, CLUSTER_DELETED)
620 97d45f69 Antony Chazapis
                if del_props is None:
621 7efc9f86 Sofia Papagiannaki
                    raise ItemNotExists('Object does not exist')
622 97d45f69 Antony Chazapis
                modified = del_props[self.MTIME]
623 a9b3f29d Antony Chazapis
        
624 82482e2c Antony Chazapis
        meta = {}
625 82482e2c Antony Chazapis
        if include_user_defined:
626 82482e2c Antony Chazapis
            meta.update(dict(self.node.attribute_get(props[self.SERIAL], domain)))
627 33b4e4a6 Antony Chazapis
        meta.update({'name': name,
628 33b4e4a6 Antony Chazapis
                     'bytes': props[self.SIZE],
629 33b4e4a6 Antony Chazapis
                     'type': props[self.TYPE],
630 371d907a Antony Chazapis
                     'hash': props[self.HASH],
631 33b4e4a6 Antony Chazapis
                     'version': props[self.SERIAL],
632 33b4e4a6 Antony Chazapis
                     'version_timestamp': props[self.MTIME],
633 33b4e4a6 Antony Chazapis
                     'modified': modified,
634 33b4e4a6 Antony Chazapis
                     'modified_by': props[self.MUSER],
635 33b4e4a6 Antony Chazapis
                     'uuid': props[self.UUID],
636 33b4e4a6 Antony Chazapis
                     'checksum': props[self.CHECKSUM]})
637 a9b3f29d Antony Chazapis
        return meta
638 a9b3f29d Antony Chazapis
    
639 a9b3f29d Antony Chazapis
    @backend_method
640 cb69c154 Antony Chazapis
    def update_object_meta(self, user, account, container, name, domain, meta, replace=False):
641 cb69c154 Antony Chazapis
        """Update the metadata associated with the object for the domain and return the new version."""
642 a9b3f29d Antony Chazapis
        
643 fe2db49d Sofia Papagiannaki
        logger.debug("update_object_meta: %s %s %s %s %s %s %s", user, account, container, name, domain, meta, replace)
644 a9b3f29d Antony Chazapis
        self._can_write(user, account, container, name)
645 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
646 4819d34f Antony Chazapis
        src_version_id, dest_version_id = self._put_metadata(user, node, domain, meta, replace)
647 5cc484e1 Antony Chazapis
        self._apply_versioning(account, container, src_version_id)
648 5cc484e1 Antony Chazapis
        return dest_version_id
649 a9b3f29d Antony Chazapis
    
650 a9b3f29d Antony Chazapis
    @backend_method
651 a9b3f29d Antony Chazapis
    def get_object_permissions(self, user, account, container, name):
652 067cf1fc Antony Chazapis
        """Return the action allowed on the object, the path
653 067cf1fc Antony Chazapis
        from which the object gets its permissions from,
654 a9b3f29d Antony Chazapis
        along with a dictionary containing the permissions."""
655 a9b3f29d Antony Chazapis
        
656 fe2db49d Sofia Papagiannaki
        logger.debug("get_object_permissions: %s %s %s %s", user, account, container, name)
657 067cf1fc Antony Chazapis
        allowed = 'write'
658 92da0e5a Antony Chazapis
        permissions_path = self._get_permissions_path(account, container, name)
659 067cf1fc Antony Chazapis
        if user != account:
660 92da0e5a Antony Chazapis
            if self.permissions.access_check(permissions_path, self.WRITE, user):
661 067cf1fc Antony Chazapis
                allowed = 'write'
662 92da0e5a Antony Chazapis
            elif self.permissions.access_check(permissions_path, self.READ, user):
663 067cf1fc Antony Chazapis
                allowed = 'read'
664 067cf1fc Antony Chazapis
            else:
665 067cf1fc Antony Chazapis
                raise NotAllowedError
666 92da0e5a Antony Chazapis
        self._lookup_object(account, container, name)
667 92da0e5a Antony Chazapis
        return (allowed, permissions_path, self.permissions.access_get(permissions_path))
668 a9b3f29d Antony Chazapis
    
669 a9b3f29d Antony Chazapis
    @backend_method
670 a9b3f29d Antony Chazapis
    def update_object_permissions(self, user, account, container, name, permissions):
671 a9b3f29d Antony Chazapis
        """Update the permissions associated with the object."""
672 a9b3f29d Antony Chazapis
        
673 fe2db49d Sofia Papagiannaki
        logger.debug("update_object_permissions: %s %s %s %s %s", user, account, container, name, permissions)
674 a9b3f29d Antony Chazapis
        if user != account:
675 a9b3f29d Antony Chazapis
            raise NotAllowedError
676 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
677 6f4bce7b Antony Chazapis
        self._check_permissions(path, permissions)
678 0f9d752c Antony Chazapis
        self.permissions.access_set(path, permissions)
679 a74ba506 Sofia Papagiannaki
        self._report_sharing_change(user, account, path, {'members':self.permissions.access_members(path)})
680 a9b3f29d Antony Chazapis
    
681 a9b3f29d Antony Chazapis
    @backend_method
682 a9b3f29d Antony Chazapis
    def get_object_public(self, user, account, container, name):
683 bb4eafc6 Antony Chazapis
        """Return the public id of the object if applicable."""
684 a9b3f29d Antony Chazapis
        
685 fe2db49d Sofia Papagiannaki
        logger.debug("get_object_public: %s %s %s %s", user, account, container, name)
686 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
687 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
688 bb4eafc6 Antony Chazapis
        p = self.permissions.public_get(path)
689 bb4eafc6 Antony Chazapis
        if p is not None:
690 bb4eafc6 Antony Chazapis
            p += ULTIMATE_ANSWER
691 bb4eafc6 Antony Chazapis
        return p
692 a9b3f29d Antony Chazapis
    
693 a9b3f29d Antony Chazapis
    @backend_method
694 a9b3f29d Antony Chazapis
    def update_object_public(self, user, account, container, name, public):
695 a9b3f29d Antony Chazapis
        """Update the public status of the object."""
696 a9b3f29d Antony Chazapis
        
697 fe2db49d Sofia Papagiannaki
        logger.debug("update_object_public: %s %s %s %s %s", user, account, container, name, public)
698 a9b3f29d Antony Chazapis
        self._can_write(user, account, container, name)
699 c915d3bf Antony Chazapis
        path = self._lookup_object(account, container, name)[0]
700 0f9d752c Antony Chazapis
        if not public:
701 0f9d752c Antony Chazapis
            self.permissions.public_unset(path)
702 0f9d752c Antony Chazapis
        else:
703 0f9d752c Antony Chazapis
            self.permissions.public_set(path)
704 a9b3f29d Antony Chazapis
    
705 a9b3f29d Antony Chazapis
    @backend_method
706 a9b3f29d Antony Chazapis
    def get_object_hashmap(self, user, account, container, name, version=None):
707 a9b3f29d Antony Chazapis
        """Return the object's size and a list with partial hashes."""
708 a9b3f29d Antony Chazapis
        
709 fe2db49d Sofia Papagiannaki
        logger.debug("get_object_hashmap: %s %s %s %s %s", user, account, container, name, version)
710 a9b3f29d Antony Chazapis
        self._can_read(user, account, container, name)
711 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
712 c915d3bf Antony Chazapis
        props = self._get_version(node, version)
713 7ca7bb08 Antony Chazapis
        hashmap = self.store.map_get(binascii.unhexlify(props[self.HASH]))
714 2c5363a0 Antony Chazapis
        return props[self.SIZE], [binascii.hexlify(x) for x in hashmap]
715 a9b3f29d Antony Chazapis
    
716 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):
717 b9064632 Antony Chazapis
        if permissions is not None and user != account:
718 b9064632 Antony Chazapis
            raise NotAllowedError
719 b9064632 Antony Chazapis
        self._can_write(user, account, container, name)
720 b9064632 Antony Chazapis
        if permissions is not None:
721 b9064632 Antony Chazapis
            path = '/'.join((account, container, name))
722 b9064632 Antony Chazapis
            self._check_permissions(path, permissions)
723 b9064632 Antony Chazapis
        
724 b9064632 Antony Chazapis
        account_path, account_node = self._lookup_account(account, True)
725 b9064632 Antony Chazapis
        container_path, container_node = self._lookup_container(account, container)
726 b9064632 Antony Chazapis
        path, node = self._put_object_node(container_path, container_node, name)
727 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)
728 b9064632 Antony Chazapis
        
729 f9ea264b Antony Chazapis
        # Handle meta.
730 f9ea264b Antony Chazapis
        if src_version_id is None:
731 f9ea264b Antony Chazapis
            src_version_id = pre_version_id
732 f9ea264b Antony Chazapis
        self._put_metadata_duplicate(src_version_id, dest_version_id, domain, meta, replace_meta)
733 f9ea264b Antony Chazapis
        
734 b9064632 Antony Chazapis
        # Check quota.
735 813e42e5 Antony Chazapis
        del_size = self._apply_versioning(account, container, pre_version_id)
736 813e42e5 Antony Chazapis
        size_delta = size - del_size
737 b9064632 Antony Chazapis
        if size_delta > 0:
738 8693b873 Sofia Papagiannaki
            account_quota = long(self._get_policy(account_node)['quota'])
739 8693b873 Sofia Papagiannaki
            container_quota = long(self._get_policy(container_node)['quota'])
740 0df22aea Sofia Papagiannaki
            if (account_quota > 0 and self._get_statistics(account_node)[1] + size_delta > account_quota) or \
741 0df22aea Sofia Papagiannaki
               (container_quota > 0 and self._get_statistics(container_node)[1] + size_delta > container_quota):
742 b9064632 Antony Chazapis
                # This must be executed in a transaction, so the version is never created if it fails.
743 5df6c6d1 Antony Chazapis
                raise QuotaError
744 b82d3277 Sofia Papagiannaki
        self._report_size_change(user, account, size_delta, {'action': 'object update', 'path':path})
745 b9064632 Antony Chazapis
        
746 b9064632 Antony Chazapis
        if permissions is not None:
747 b9064632 Antony Chazapis
            self.permissions.access_set(path, permissions)
748 a74ba506 Sofia Papagiannaki
            self._report_sharing_change(user, account, path, {'members':self.permissions.access_members(path)})
749 39ef6f41 Antony Chazapis
        
750 39ef6f41 Antony Chazapis
        self._report_object_change(user, account, path, details={'version': dest_version_id, 'action': 'object update'})
751 f9ea264b Antony Chazapis
        return dest_version_id
752 b9064632 Antony Chazapis
    
753 a9b3f29d Antony Chazapis
    @backend_method
754 33b4e4a6 Antony Chazapis
    def update_object_hashmap(self, user, account, container, name, size, type, hashmap, checksum, domain, meta={}, replace_meta=False, permissions=None):
755 a9b3f29d Antony Chazapis
        """Create/update an object with the specified size and partial hashes."""
756 a9b3f29d Antony Chazapis
        
757 fe2db49d Sofia Papagiannaki
        logger.debug("update_object_hashmap: %s %s %s %s %s %s %s %s", user, account, container, name, size, type, hashmap, checksum)
758 6d64339e Antony Chazapis
        if size == 0: # No such thing as an empty hashmap.
759 6d64339e Antony Chazapis
            hashmap = [self.put_block('')]
760 1c2fc0ff Antony Chazapis
        map = HashMap(self.block_size, self.hash_algorithm)
761 1c2fc0ff Antony Chazapis
        map.extend([binascii.unhexlify(x) for x in hashmap])
762 7ca7bb08 Antony Chazapis
        missing = self.store.block_search(map)
763 a9b3f29d Antony Chazapis
        if missing:
764 a9b3f29d Antony Chazapis
            ie = IndexError()
765 dd71f493 Antony Chazapis
            ie.data = [binascii.hexlify(x) for x in missing]
766 a9b3f29d Antony Chazapis
            raise ie
767 b9064632 Antony Chazapis
        
768 1c2fc0ff Antony Chazapis
        hash = map.hash()
769 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)
770 7ca7bb08 Antony Chazapis
        self.store.map_put(hash, map)
771 02c4d2ba Antony Chazapis
        return dest_version_id
772 a9b3f29d Antony Chazapis
    
773 33b4e4a6 Antony Chazapis
    @backend_method
774 33b4e4a6 Antony Chazapis
    def update_object_checksum(self, user, account, container, name, version, checksum):
775 33b4e4a6 Antony Chazapis
        """Update an object's checksum."""
776 33b4e4a6 Antony Chazapis
        
777 fe2db49d Sofia Papagiannaki
        logger.debug("update_object_checksum: %s %s %s %s %s %s", user, account, container, name, version, checksum)
778 33b4e4a6 Antony Chazapis
        # Update objects with greater version and same hashmap and size (fix metadata updates).
779 33b4e4a6 Antony Chazapis
        self._can_write(user, account, container, name)
780 33b4e4a6 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
781 33b4e4a6 Antony Chazapis
        props = self._get_version(node, version)
782 33b4e4a6 Antony Chazapis
        versions = self.node.node_get_versions(node)
783 33b4e4a6 Antony Chazapis
        for x in versions:
784 33b4e4a6 Antony Chazapis
            if x[self.SERIAL] >= int(version) and x[self.HASH] == props[self.HASH] and x[self.SIZE] == props[self.SIZE]:
785 33b4e4a6 Antony Chazapis
                self.node.version_put_property(x[self.SERIAL], 'checksum', checksum)
786 33b4e4a6 Antony Chazapis
    
787 4d15c94e 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={}, replace_meta=False, permissions=None, src_version=None, is_move=False, delimiter=None):
788 4d15c94e Sofia Papagiannaki
        dest_version_ids = []
789 79bb41b7 Antony Chazapis
        self._can_read(user, src_account, src_container, src_name)
790 b9064632 Antony Chazapis
        path, node = self._lookup_object(src_account, src_container, src_name)
791 1730b3bf chazapis
        # TODO: Will do another fetch of the properties in duplicate version...
792 1730b3bf chazapis
        props = self._get_version(node, src_version) # Check to see if source exists.
793 b9064632 Antony Chazapis
        src_version_id = props[self.SERIAL]
794 b9064632 Antony Chazapis
        hash = props[self.HASH]
795 b9064632 Antony Chazapis
        size = props[self.SIZE]
796 25ae8b75 Antony Chazapis
        is_copy = not is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name) # New uuid.
797 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))
798 3f767854 Sofia Papagiannaki
        if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
799 4d15c94e Sofia Papagiannaki
                self._delete_object(user, src_account, src_container, src_name)
800 4d15c94e Sofia Papagiannaki
        
801 4d15c94e Sofia Papagiannaki
        if delimiter:
802 4d15c94e Sofia Papagiannaki
            prefix = src_name + delimiter if not src_name.endswith(delimiter) else src_name
803 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)
804 07867f70 Sofia Papagiannaki
            src_names.sort(key=lambda x: x[2]) # order by nodes
805 4d15c94e Sofia Papagiannaki
            paths = [elem[0] for elem in src_names]
806 4d15c94e Sofia Papagiannaki
            nodes = [elem[2] for elem in src_names]
807 4d15c94e Sofia Papagiannaki
            # TODO: Will do another fetch of the properties in duplicate version...
808 4d15c94e Sofia Papagiannaki
            props = self._get_versions(nodes) # Check to see if source exists.
809 4d15c94e Sofia Papagiannaki
            
810 4d15c94e Sofia Papagiannaki
            for prop, path, node in zip(props, paths, nodes):
811 4d15c94e Sofia Papagiannaki
                src_version_id = prop[self.SERIAL]
812 4d15c94e Sofia Papagiannaki
                hash = prop[self.HASH]
813 4d15c94e Sofia Papagiannaki
                vtype = prop[self.TYPE]
814 07867f70 Sofia Papagiannaki
                size = prop[self.SIZE]
815 4d15c94e Sofia Papagiannaki
                dest_prefix = dest_name + delimiter if not dest_name.endswith(delimiter) else dest_name
816 4d15c94e Sofia Papagiannaki
                vdest_name = path.replace(prefix, dest_prefix, 1)
817 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))
818 3f767854 Sofia Papagiannaki
                if is_move and (src_account, src_container, src_name) != (dest_account, dest_container, dest_name):
819 4d15c94e Sofia Papagiannaki
                        self._delete_object(user, src_account, src_container, path)
820 4d15c94e Sofia Papagiannaki
        return dest_version_ids[0] if len(dest_version_ids) == 1 else dest_version_ids
821 4d15c94e Sofia Papagiannaki
    
822 4d15c94e Sofia Papagiannaki
    @backend_method
823 4d15c94e Sofia Papagiannaki
    def copy_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta={}, replace_meta=False, permissions=None, src_version=None, delimiter=None):
824 dff7b6f1 Sofia Papagiannaki
        """Copy an object's data and metadata."""
825 dff7b6f1 Sofia Papagiannaki
        
826 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)
827 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)
828 46286f5f Antony Chazapis
        return dest_version_id
829 dff7b6f1 Sofia Papagiannaki
    
830 dff7b6f1 Sofia Papagiannaki
    @backend_method
831 4d15c94e Sofia Papagiannaki
    def move_object(self, user, src_account, src_container, src_name, dest_account, dest_container, dest_name, type, domain, meta={}, replace_meta=False, permissions=None, delimiter=None):
832 a9b3f29d Antony Chazapis
        """Move an object's data and metadata."""
833 a9b3f29d Antony Chazapis
        
834 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)
835 79bb41b7 Antony Chazapis
        if user != src_account:
836 79bb41b7 Antony Chazapis
            raise NotAllowedError
837 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)
838 02c4d2ba Antony Chazapis
        return dest_version_id
839 a9b3f29d Antony Chazapis
    
840 4d15c94e Sofia Papagiannaki
    def _delete_object(self, user, account, container, name, until=None, delimiter=None):
841 a9b3f29d Antony Chazapis
        if user != account:
842 a9b3f29d Antony Chazapis
            raise NotAllowedError
843 a9b3f29d Antony Chazapis
        
844 a9b3f29d Antony Chazapis
        if until is not None:
845 a9b3f29d Antony Chazapis
            path = '/'.join((account, container, name))
846 c915d3bf Antony Chazapis
            node = self.node.node_lookup(path)
847 c915d3bf Antony Chazapis
            if node is None:
848 c915d3bf Antony Chazapis
                return
849 813e42e5 Antony Chazapis
            hashes = []
850 813e42e5 Antony Chazapis
            size = 0
851 813e42e5 Antony Chazapis
            h, s = self.node.node_purge(node, until, CLUSTER_NORMAL)
852 813e42e5 Antony Chazapis
            hashes += h
853 813e42e5 Antony Chazapis
            size += s
854 813e42e5 Antony Chazapis
            h, s = self.node.node_purge(node, until, CLUSTER_HISTORY)
855 813e42e5 Antony Chazapis
            hashes += h
856 813e42e5 Antony Chazapis
            size += s
857 04230536 Antony Chazapis
            for h in hashes:
858 04230536 Antony Chazapis
                self.store.map_delete(h)
859 4a1c29ea Antony Chazapis
            self.node.node_purge(node, until, CLUSTER_DELETED)
860 a9b3f29d Antony Chazapis
            try:
861 c915d3bf Antony Chazapis
                props = self._get_version(node)
862 a9b3f29d Antony Chazapis
            except NameError:
863 0f9d752c Antony Chazapis
                self.permissions.access_clear(path)
864 b82d3277 Sofia Papagiannaki
            self._report_size_change(user, account, -size, {'action': 'object purge', 'path':path})
865 a9b3f29d Antony Chazapis
            return
866 a9b3f29d Antony Chazapis
        
867 c915d3bf Antony Chazapis
        path, node = self._lookup_object(account, container, name)
868 33b4e4a6 Antony Chazapis
        src_version_id, dest_version_id = self._put_version_duplicate(user, node, size=0, type='', hash=None, checksum='', cluster=CLUSTER_DELETED)
869 813e42e5 Antony Chazapis
        del_size = self._apply_versioning(account, container, src_version_id)
870 813e42e5 Antony Chazapis
        if del_size:
871 b82d3277 Sofia Papagiannaki
            self._report_size_change(user, account, -del_size, {'action': 'object delete', 'path':path})
872 39ef6f41 Antony Chazapis
        self._report_object_change(user, account, path, details={'action': 'object delete'})
873 0f9d752c Antony Chazapis
        self.permissions.access_clear(path)
874 4d15c94e Sofia Papagiannaki
        
875 4d15c94e Sofia Papagiannaki
        if delimiter:
876 4d15c94e Sofia Papagiannaki
            prefix = name + delimiter if not name.endswith(delimiter) else name
877 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)
878 4d15c94e Sofia Papagiannaki
            paths = []
879 4d15c94e Sofia Papagiannaki
            for t in src_names:
880 4d15c94e Sofia Papagiannaki
                    path = '/'.join((account, container, t[0]))
881 4d15c94e Sofia Papagiannaki
                    node = t[2]
882 4d15c94e Sofia Papagiannaki
                src_version_id, dest_version_id = self._put_version_duplicate(user, node, size=0, type='', hash=None, checksum='', cluster=CLUSTER_DELETED)
883 4d15c94e Sofia Papagiannaki
                del_size = self._apply_versioning(account, container, src_version_id)
884 4d15c94e Sofia Papagiannaki
                if del_size:
885 b82d3277 Sofia Papagiannaki
                    self._report_size_change(user, account, -del_size, {'action': 'object delete', 'path':path})
886 4d15c94e Sofia Papagiannaki
                self._report_object_change(user, account, path, details={'action': 'object delete'})
887 4d15c94e Sofia Papagiannaki
                paths.append(path)
888 4d15c94e Sofia Papagiannaki
            self.permissions.access_clear_bulk(paths)
889 4d15c94e Sofia Papagiannaki
    
890 4d15c94e Sofia Papagiannaki
    @backend_method
891 4d15c94e Sofia Papagiannaki
    def delete_object(self, user, account, container, name, until=None, prefix='', delimiter=None):
892 dff7b6f1 Sofia Papagiannaki
        """Delete/purge an object."""
893 dff7b6f1 Sofia Papagiannaki
        
894 fe2db49d Sofia Papagiannaki
        logger.debug("delete_object: %s %s %s %s %s %s %s", user, account, container, name, until, prefix, delimiter)
895 4d15c94e Sofia Papagiannaki
        self._delete_object(user, account, container, name, until, delimiter)
896 dff7b6f1 Sofia Papagiannaki
    
897 dff7b6f1 Sofia Papagiannaki
    @backend_method
898 62f915a1 Antony Chazapis
    def list_versions(self, user, account, container, name):
899 62f915a1 Antony Chazapis
        """Return a list of all (version, version_timestamp) tuples for an object."""
900 62f915a1 Antony Chazapis
        
901 fe2db49d Sofia Papagiannaki
        logger.debug("list_versions: %s %s %s %s", user, account, container, name)
902 62f915a1 Antony Chazapis
        self._can_read(user, account, container, name)
903 60b8a083 Antony Chazapis
        path, node = self._lookup_object(account, container, name)
904 97d45f69 Antony Chazapis
        versions = self.node.node_get_versions(node)
905 97d45f69 Antony Chazapis
        return [[x[self.SERIAL], x[self.MTIME]] for x in versions if x[self.CLUSTER] != CLUSTER_DELETED]
906 a9b3f29d Antony Chazapis
    
907 bb4eafc6 Antony Chazapis
    @backend_method
908 37bee317 Antony Chazapis
    def get_uuid(self, user, uuid):
909 37bee317 Antony Chazapis
        """Return the (account, container, name) for the UUID given."""
910 af75e8a5 Antony Chazapis
        
911 fe2db49d Sofia Papagiannaki
        logger.debug("get_uuid: %s %s", user, uuid)
912 37bee317 Antony Chazapis
        info = self.node.latest_uuid(uuid)
913 37bee317 Antony Chazapis
        if info is None:
914 37bee317 Antony Chazapis
            raise NameError
915 37bee317 Antony Chazapis
        path, serial = info
916 37bee317 Antony Chazapis
        account, container, name = path.split('/', 2)
917 37bee317 Antony Chazapis
        self._can_read(user, account, container, name)
918 37bee317 Antony Chazapis
        return (account, container, name)
919 37bee317 Antony Chazapis
    
920 37bee317 Antony Chazapis
    @backend_method
921 bb4eafc6 Antony Chazapis
    def get_public(self, user, public):
922 bb4eafc6 Antony Chazapis
        """Return the (account, container, name) for the public id given."""
923 af75e8a5 Antony Chazapis
        
924 fe2db49d Sofia Papagiannaki
        logger.debug("get_public: %s %s", user, public)
925 bb4eafc6 Antony Chazapis
        if public is None or public < ULTIMATE_ANSWER:
926 bb4eafc6 Antony Chazapis
            raise NameError
927 bb4eafc6 Antony Chazapis
        path = self.permissions.public_path(public - ULTIMATE_ANSWER)
928 37bee317 Antony Chazapis
        if path is None:
929 37bee317 Antony Chazapis
            raise NameError
930 bb4eafc6 Antony Chazapis
        account, container, name = path.split('/', 2)
931 bb4eafc6 Antony Chazapis
        self._can_read(user, account, container, name)
932 bb4eafc6 Antony Chazapis
        return (account, container, name)
933 bb4eafc6 Antony Chazapis
    
934 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
935 a9b3f29d Antony Chazapis
    def get_block(self, hash):
936 a9b3f29d Antony Chazapis
        """Return a block's data."""
937 a9b3f29d Antony Chazapis
        
938 a9b3f29d Antony Chazapis
        logger.debug("get_block: %s", hash)
939 7ca7bb08 Antony Chazapis
        block = self.store.block_get(binascii.unhexlify(hash))
940 7ca7bb08 Antony Chazapis
        if not block:
941 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Block does not exist')
942 7ca7bb08 Antony Chazapis
        return block
943 a9b3f29d Antony Chazapis
    
944 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
945 a9b3f29d Antony Chazapis
    def put_block(self, data):
946 60b8a083 Antony Chazapis
        """Store a block and return the hash."""
947 a9b3f29d Antony Chazapis
        
948 a9b3f29d Antony Chazapis
        logger.debug("put_block: %s", len(data))
949 7ca7bb08 Antony Chazapis
        return binascii.hexlify(self.store.block_put(data))
950 a9b3f29d Antony Chazapis
    
951 a9b3f29d Antony Chazapis
    @backend_method(autocommit=0)
952 a9b3f29d Antony Chazapis
    def update_block(self, hash, data, offset=0):
953 a9b3f29d Antony Chazapis
        """Update a known block and return the hash."""
954 a9b3f29d Antony Chazapis
        
955 a9b3f29d Antony Chazapis
        logger.debug("update_block: %s %s %s", hash, len(data), offset)
956 a9b3f29d Antony Chazapis
        if offset == 0 and len(data) == self.block_size:
957 a9b3f29d Antony Chazapis
            return self.put_block(data)
958 7ca7bb08 Antony Chazapis
        h = self.store.block_update(binascii.unhexlify(hash), offset, data)
959 a9b3f29d Antony Chazapis
        return binascii.hexlify(h)
960 a9b3f29d Antony Chazapis
    
961 44ad5860 Antony Chazapis
    # Path functions.
962 44ad5860 Antony Chazapis
    
963 37bee317 Antony Chazapis
    def _generate_uuid(self):
964 37bee317 Antony Chazapis
        return str(uuidlib.uuid4())
965 25ae8b75 Antony Chazapis
    
966 b9064632 Antony Chazapis
    def _put_object_node(self, path, parent, name):
967 c915d3bf Antony Chazapis
        path = '/'.join((path, name))
968 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
969 c915d3bf Antony Chazapis
        if node is None:
970 c915d3bf Antony Chazapis
            node = self.node.node_create(parent, path)
971 c915d3bf Antony Chazapis
        return path, node
972 c915d3bf Antony Chazapis
    
973 62f915a1 Antony Chazapis
    def _put_path(self, user, parent, path):
974 62f915a1 Antony Chazapis
        node = self.node.node_create(parent, path)
975 33b4e4a6 Antony Chazapis
        self.node.version_create(node, None, 0, '', None, user, self._generate_uuid(), '', CLUSTER_NORMAL)
976 62f915a1 Antony Chazapis
        return node
977 62f915a1 Antony Chazapis
    
978 44ad5860 Antony Chazapis
    def _lookup_account(self, account, create=True):
979 44ad5860 Antony Chazapis
        node = self.node.node_lookup(account)
980 44ad5860 Antony Chazapis
        if node is None and create:
981 2c5363a0 Antony Chazapis
            node = self._put_path(account, self.ROOTNODE, account) # User is account.
982 c915d3bf Antony Chazapis
        return account, node
983 44ad5860 Antony Chazapis
    
984 44ad5860 Antony Chazapis
    def _lookup_container(self, account, container):
985 c915d3bf Antony Chazapis
        path = '/'.join((account, container))
986 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
987 44ad5860 Antony Chazapis
        if node is None:
988 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Container does not exist')
989 c915d3bf Antony Chazapis
        return path, node
990 44ad5860 Antony Chazapis
    
991 44ad5860 Antony Chazapis
    def _lookup_object(self, account, container, name):
992 c915d3bf Antony Chazapis
        path = '/'.join((account, container, name))
993 c915d3bf Antony Chazapis
        node = self.node.node_lookup(path)
994 44ad5860 Antony Chazapis
        if node is None:
995 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Object does not exist')
996 c915d3bf Antony Chazapis
        return path, node
997 44ad5860 Antony Chazapis
    
998 cf4a7a7b Sofia Papagiannaki
    def _lookup_objects(self, paths):
999 7efc9f86 Sofia Papagiannaki
        nodes = self.node.node_lookup_bulk(paths)
1000 cf4a7a7b Sofia Papagiannaki
        return paths, nodes
1001 cf4a7a7b Sofia Papagiannaki
    
1002 44ad5860 Antony Chazapis
    def _get_properties(self, node, until=None):
1003 44ad5860 Antony Chazapis
        """Return properties until the timestamp given."""
1004 44ad5860 Antony Chazapis
        
1005 44ad5860 Antony Chazapis
        before = until if until is not None else inf
1006 44ad5860 Antony Chazapis
        props = self.node.version_lookup(node, before, CLUSTER_NORMAL)
1007 44ad5860 Antony Chazapis
        if props is None and until is not None:
1008 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, before, CLUSTER_HISTORY)
1009 44ad5860 Antony Chazapis
        if props is None:
1010 7efc9f86 Sofia Papagiannaki
            raise ItemNotExists('Path does not exist')
1011 44ad5860 Antony Chazapis
        return props
1012 44ad5860 Antony Chazapis
    
1013 62f915a1 Antony Chazapis
    def _get_statistics(self, node, until=None):
1014 62f915a1 Antony Chazapis
        """Return count, sum of size and latest timestamp of everything under node."""
1015 c915d3bf Antony Chazapis
        
1016 44ad5860 Antony Chazapis
        if until is None:
1017 62f915a1 Antony Chazapis
            stats = self.node.statistics_get(node, CLUSTER_NORMAL)
1018 44ad5860 Antony Chazapis
        else:
1019 62f915a1 Antony Chazapis
            stats = self.node.statistics_latest(node, until, CLUSTER_DELETED)
1020 62f915a1 Antony Chazapis
        if stats is None:
1021 62f915a1 Antony Chazapis
            stats = (0, 0, 0)
1022 62f915a1 Antony Chazapis
        return stats
1023 44ad5860 Antony Chazapis
    
1024 44ad5860 Antony Chazapis
    def _get_version(self, node, version=None):
1025 44ad5860 Antony Chazapis
        if version is None:
1026 44ad5860 Antony Chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1027 44ad5860 Antony Chazapis
            if props is None:
1028 7efc9f86 Sofia Papagiannaki
                raise ItemNotExists('Object does not exist')
1029 44ad5860 Antony Chazapis
        else:
1030 07afd277 Antony Chazapis
            try:
1031 07afd277 Antony Chazapis
                version = int(version)
1032 07afd277 Antony Chazapis
            except ValueError:
1033 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1034 44ad5860 Antony Chazapis
            props = self.node.version_get_properties(version)
1035 2c5363a0 Antony Chazapis
            if props is None or props[self.CLUSTER] == CLUSTER_DELETED:
1036 7efc9f86 Sofia Papagiannaki
                raise VersionNotExists('Version does not exist')
1037 44ad5860 Antony Chazapis
        return props
1038 4d15c94e Sofia Papagiannaki
1039 7efc9f86 Sofia Papagiannaki
    def _get_versions(self, nodes):
1040 7efc9f86 Sofia Papagiannaki
        return self.node.version_lookup_bulk(nodes, inf, CLUSTER_NORMAL)
1041 44ad5860 Antony Chazapis
    
1042 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):
1043 b9064632 Antony Chazapis
        """Create a new version of the node."""
1044 c915d3bf Antony Chazapis
        
1045 1730b3bf chazapis
        props = self.node.version_lookup(node if src_node is None else src_node, inf, CLUSTER_NORMAL)
1046 b9064632 Antony Chazapis
        if props is not None:
1047 b9064632 Antony Chazapis
            src_version_id = props[self.SERIAL]
1048 b9064632 Antony Chazapis
            src_hash = props[self.HASH]
1049 b9064632 Antony Chazapis
            src_size = props[self.SIZE]
1050 66ce2ca5 Antony Chazapis
            src_type = props[self.TYPE]
1051 33b4e4a6 Antony Chazapis
            src_checksum = props[self.CHECKSUM]
1052 44ad5860 Antony Chazapis
        else:
1053 b9064632 Antony Chazapis
            src_version_id = None
1054 b9064632 Antony Chazapis
            src_hash = None
1055 b9064632 Antony Chazapis
            src_size = 0
1056 66ce2ca5 Antony Chazapis
            src_type = ''
1057 33b4e4a6 Antony Chazapis
            src_checksum = ''
1058 66ce2ca5 Antony Chazapis
        if size is None: # Set metadata.
1059 66ce2ca5 Antony Chazapis
            hash = src_hash # This way hash can be set to None (account or container).
1060 b9064632 Antony Chazapis
            size = src_size
1061 66ce2ca5 Antony Chazapis
        if type is None:
1062 66ce2ca5 Antony Chazapis
            type = src_type
1063 33b4e4a6 Antony Chazapis
        if checksum is None:
1064 33b4e4a6 Antony Chazapis
            checksum = src_checksum
1065 1730b3bf chazapis
        uuid = self._generate_uuid() if (is_copy or src_version_id is None) else props[self.UUID]
1066 1730b3bf chazapis
        
1067 1730b3bf chazapis
        if src_node is None:
1068 1730b3bf chazapis
            pre_version_id = src_version_id
1069 1730b3bf chazapis
        else:
1070 1730b3bf chazapis
            pre_version_id = None
1071 1730b3bf chazapis
            props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1072 1730b3bf chazapis
            if props is not None:
1073 1730b3bf chazapis
                pre_version_id = props[self.SERIAL]
1074 1730b3bf chazapis
        if pre_version_id is not None:
1075 1730b3bf chazapis
            self.node.version_recluster(pre_version_id, CLUSTER_HISTORY)
1076 b9064632 Antony Chazapis
        
1077 33b4e4a6 Antony Chazapis
        dest_version_id, mtime = self.node.version_create(node, hash, size, type, src_version_id, user, uuid, checksum, cluster)
1078 1730b3bf chazapis
        return pre_version_id, dest_version_id
1079 44ad5860 Antony Chazapis
    
1080 4819d34f Antony Chazapis
    def _put_metadata_duplicate(self, src_version_id, dest_version_id, domain, meta, replace=False):
1081 4819d34f Antony Chazapis
        if src_version_id is not None:
1082 4819d34f Antony Chazapis
            self.node.attribute_copy(src_version_id, dest_version_id)
1083 4819d34f Antony Chazapis
        if not replace:
1084 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain, (k for k, v in meta.iteritems() if v == ''))
1085 4819d34f Antony Chazapis
            self.node.attribute_set(dest_version_id, domain, ((k, v) for k, v in meta.iteritems() if v != ''))
1086 4819d34f Antony Chazapis
        else:
1087 4819d34f Antony Chazapis
            self.node.attribute_del(dest_version_id, domain)
1088 4819d34f Antony Chazapis
            self.node.attribute_set(dest_version_id, domain, ((k, v) for k, v in meta.iteritems()))
1089 4819d34f Antony Chazapis
    
1090 4819d34f Antony Chazapis
    def _put_metadata(self, user, node, domain, meta, replace=False):
1091 44ad5860 Antony Chazapis
        """Create a new version and store metadata."""
1092 44ad5860 Antony Chazapis
        
1093 b9064632 Antony Chazapis
        src_version_id, dest_version_id = self._put_version_duplicate(user, node)
1094 4819d34f Antony Chazapis
        self._put_metadata_duplicate(src_version_id, dest_version_id, domain, meta, replace)
1095 5cc484e1 Antony Chazapis
        return src_version_id, dest_version_id
1096 44ad5860 Antony Chazapis
    
1097 60b8a083 Antony Chazapis
    def _list_limits(self, listing, marker, limit):
1098 60b8a083 Antony Chazapis
        start = 0
1099 60b8a083 Antony Chazapis
        if marker:
1100 60b8a083 Antony Chazapis
            try:
1101 60b8a083 Antony Chazapis
                start = listing.index(marker) + 1
1102 60b8a083 Antony Chazapis
            except ValueError:
1103 60b8a083 Antony Chazapis
                pass
1104 60b8a083 Antony Chazapis
        if not limit or limit > 10000:
1105 60b8a083 Antony Chazapis
            limit = 10000
1106 60b8a083 Antony Chazapis
        return start, limit
1107 60b8a083 Antony Chazapis
    
1108 371d907a Antony Chazapis
    def _list_object_properties(self, parent, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, domain=None, keys=[], until=None, size_range=None, allowed=[], all_props=False):
1109 60b8a083 Antony Chazapis
        cont_prefix = path + '/'
1110 60b8a083 Antony Chazapis
        prefix = cont_prefix + prefix
1111 60b8a083 Antony Chazapis
        start = cont_prefix + marker if marker else None
1112 60b8a083 Antony Chazapis
        before = until if until is not None else inf
1113 4819d34f Antony Chazapis
        filterq = keys if domain else []
1114 7ff57991 Antony Chazapis
        sizeq = size_range
1115 60b8a083 Antony Chazapis
        
1116 371d907a Antony Chazapis
        objects, prefixes = self.node.latest_version_list(parent, prefix, delimiter, start, limit, before, CLUSTER_DELETED, allowed, domain, filterq, sizeq, all_props)
1117 60b8a083 Antony Chazapis
        objects.extend([(p, None) for p in prefixes] if virtual else [])
1118 43be9afd Sofia Papagiannaki
        objects.sort(key=lambda x: x[0])
1119 371d907a Antony Chazapis
        objects = [(x[0][len(cont_prefix):],) + x[1:] for x in objects]
1120 cf4a7a7b Sofia Papagiannaki
        return objects
1121 60b8a083 Antony Chazapis
        
1122 813e42e5 Antony Chazapis
    # Reporting functions.
1123 813e42e5 Antony Chazapis
    
1124 813e42e5 Antony Chazapis
    def _report_size_change(self, user, account, size, details={}):
1125 813e42e5 Antony Chazapis
        account_node = self._lookup_account(account, True)[1]
1126 813e42e5 Antony Chazapis
        total = self._get_statistics(account_node)[1]
1127 813e42e5 Antony Chazapis
        details.update({'user': user, 'total': total})
1128 b82d3277 Sofia Papagiannaki
        logger.debug("_report_size_change: %s %s %s %s", user, account, size, details)
1129 73673127 Antony Chazapis
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('resource.diskspace',), account, QUEUE_INSTANCE_ID, 'diskspace', float(size), details))
1130 39ef6f41 Antony Chazapis
    
1131 39ef6f41 Antony Chazapis
    def _report_object_change(self, user, account, path, details={}):
1132 b82d3277 Sofia Papagiannaki
        details.update({'user': user})
1133 39ef6f41 Antony Chazapis
        logger.debug("_report_object_change: %s %s %s %s", user, account, path, details)
1134 73673127 Antony Chazapis
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('object',), account, QUEUE_INSTANCE_ID, 'object', path, details))
1135 813e42e5 Antony Chazapis
    
1136 a74ba506 Sofia Papagiannaki
    def _report_sharing_change(self, user, account, path, details={}):
1137 a74ba506 Sofia Papagiannaki
        logger.debug("_report_permissions_change: %s %s %s %s", user, account, path, details)
1138 a74ba506 Sofia Papagiannaki
        details.update({'user': user})
1139 a74ba506 Sofia Papagiannaki
        self.messages.append((QUEUE_MESSAGE_KEY_PREFIX % ('sharing',), account, QUEUE_INSTANCE_ID, 'sharing', path, details))
1140 a74ba506 Sofia Papagiannaki
    
1141 60b8a083 Antony Chazapis
    # Policy functions.
1142 60b8a083 Antony Chazapis
    
1143 60b8a083 Antony Chazapis
    def _check_policy(self, policy):
1144 60b8a083 Antony Chazapis
        for k in policy.keys():
1145 60b8a083 Antony Chazapis
            if policy[k] == '':
1146 60b8a083 Antony Chazapis
                policy[k] = self.default_policy.get(k)
1147 60b8a083 Antony Chazapis
        for k, v in policy.iteritems():
1148 60b8a083 Antony Chazapis
            if k == 'quota':
1149 60b8a083 Antony Chazapis
                q = int(v) # May raise ValueError.
1150 60b8a083 Antony Chazapis
                if q < 0:
1151 60b8a083 Antony Chazapis
                    raise ValueError
1152 60b8a083 Antony Chazapis
            elif k == 'versioning':
1153 5cc484e1 Antony Chazapis
                if v not in ['auto', 'none']:
1154 60b8a083 Antony Chazapis
                    raise ValueError
1155 60b8a083 Antony Chazapis
            else:
1156 60b8a083 Antony Chazapis
                raise ValueError
1157 60b8a083 Antony Chazapis
    
1158 b2832c6a Antony Chazapis
    def _put_policy(self, node, policy, replace):
1159 b2832c6a Antony Chazapis
        if replace:
1160 b2832c6a Antony Chazapis
            for k, v in self.default_policy.iteritems():
1161 b2832c6a Antony Chazapis
                if k not in policy:
1162 b2832c6a Antony Chazapis
                    policy[k] = v
1163 b2832c6a Antony Chazapis
        self.node.policy_set(node, policy)
1164 b2832c6a Antony Chazapis
    
1165 b9064632 Antony Chazapis
    def _get_policy(self, node):
1166 b9064632 Antony Chazapis
        policy = self.default_policy.copy()
1167 b9064632 Antony Chazapis
        policy.update(self.node.policy_get(node))
1168 b9064632 Antony Chazapis
        return policy
1169 b9064632 Antony Chazapis
    
1170 5cc484e1 Antony Chazapis
    def _apply_versioning(self, account, container, version_id):
1171 813e42e5 Antony Chazapis
        """Delete the provided version if such is the policy.
1172 813e42e5 Antony Chazapis
           Return size of object removed.
1173 813e42e5 Antony Chazapis
        """
1174 813e42e5 Antony Chazapis
        
1175 5cc484e1 Antony Chazapis
        if version_id is None:
1176 813e42e5 Antony Chazapis
            return 0
1177 5cc484e1 Antony Chazapis
        path, node = self._lookup_container(account, container)
1178 5cc484e1 Antony Chazapis
        versioning = self._get_policy(node)['versioning']
1179 5cc484e1 Antony Chazapis
        if versioning != 'auto':
1180 813e42e5 Antony Chazapis
            hash, size = self.node.version_remove(version_id)
1181 5161c672 Antony Chazapis
            self.store.map_delete(hash)
1182 813e42e5 Antony Chazapis
            return size
1183 813e42e5 Antony Chazapis
        return 0
1184 5cc484e1 Antony Chazapis
    
1185 a9b3f29d Antony Chazapis
    # Access control functions.
1186 a9b3f29d Antony Chazapis
    
1187 a9b3f29d Antony Chazapis
    def _check_groups(self, groups):
1188 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in groups')
1189 a9b3f29d Antony Chazapis
        pass
1190 a9b3f29d Antony Chazapis
    
1191 a9b3f29d Antony Chazapis
    def _check_permissions(self, path, permissions):
1192 0f9d752c Antony Chazapis
        # raise ValueError('Bad characters in permissions')
1193 5e068361 Antony Chazapis
        pass
1194 5e068361 Antony Chazapis
    
1195 92da0e5a Antony Chazapis
    def _get_formatted_paths(self, paths):
1196 92da0e5a Antony Chazapis
        formatted = []
1197 92da0e5a Antony Chazapis
        for p in paths:
1198 92da0e5a Antony Chazapis
            node = self.node.node_lookup(p)
1199 92da0e5a Antony Chazapis
            if node is not None:
1200 92da0e5a Antony Chazapis
                props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1201 92da0e5a Antony Chazapis
            if props is not None:
1202 692485cc Antony Chazapis
                if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1203 d57eaad4 Antony Chazapis
                    formatted.append((p.rstrip('/') + '/', self.MATCH_PREFIX))
1204 d57eaad4 Antony Chazapis
                formatted.append((p, self.MATCH_EXACT))
1205 92da0e5a Antony Chazapis
        return formatted
1206 92da0e5a Antony Chazapis
    
1207 5e068361 Antony Chazapis
    def _get_permissions_path(self, account, container, name):
1208 5e068361 Antony Chazapis
        path = '/'.join((account, container, name))
1209 5e068361 Antony Chazapis
        permission_paths = self.permissions.access_inherit(path)
1210 5e068361 Antony Chazapis
        permission_paths.sort()
1211 5e068361 Antony Chazapis
        permission_paths.reverse()
1212 5e068361 Antony Chazapis
        for p in permission_paths:
1213 5e068361 Antony Chazapis
            if p == path:
1214 5e068361 Antony Chazapis
                return p
1215 5e068361 Antony Chazapis
            else:
1216 71dbc012 Antony Chazapis
                if p.count('/') < 2:
1217 71dbc012 Antony Chazapis
                    continue
1218 92da0e5a Antony Chazapis
                node = self.node.node_lookup(p)
1219 92da0e5a Antony Chazapis
                if node is not None:
1220 92da0e5a Antony Chazapis
                    props = self.node.version_lookup(node, inf, CLUSTER_NORMAL)
1221 92da0e5a Antony Chazapis
                if props is not None:
1222 692485cc Antony Chazapis
                    if props[self.TYPE].split(';', 1)[0].strip() in ('application/directory', 'application/folder'):
1223 5e068361 Antony Chazapis
                        return p
1224 5e068361 Antony Chazapis
        return None
1225 a9b3f29d Antony Chazapis
    
1226 6f4bce7b Antony Chazapis
    def _can_read(self, user, account, container, name):
1227 a9b3f29d Antony Chazapis
        if user == account:
1228 a9b3f29d Antony Chazapis
            return True
1229 a9b3f29d Antony Chazapis
        path = '/'.join((account, container, name))
1230 aeb2b64f Antony Chazapis
        if self.permissions.public_get(path) is not None:
1231 71dbc012 Antony Chazapis
            return True
1232 5e068361 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1233 71dbc012 Antony Chazapis
        if not path:
1234 71dbc012 Antony Chazapis
            raise NotAllowedError
1235 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.READ, user) and not self.permissions.access_check(path, self.WRITE, user):
1236 a9b3f29d Antony Chazapis
            raise NotAllowedError
1237 a9b3f29d Antony Chazapis
    
1238 a9b3f29d Antony Chazapis
    def _can_write(self, user, account, container, name):
1239 6f4bce7b Antony Chazapis
        if user == account:
1240 6f4bce7b Antony Chazapis
            return True
1241 6f4bce7b Antony Chazapis
        path = '/'.join((account, container, name))
1242 71dbc012 Antony Chazapis
        path = self._get_permissions_path(account, container, name)
1243 71dbc012 Antony Chazapis
        if not path:
1244 71dbc012 Antony Chazapis
            raise NotAllowedError
1245 2c5363a0 Antony Chazapis
        if not self.permissions.access_check(path, self.WRITE, user):
1246 a9b3f29d Antony Chazapis
            raise NotAllowedError
1247 a9b3f29d Antony Chazapis
    
1248 a9b3f29d Antony Chazapis
    def _allowed_accounts(self, user):
1249 a9b3f29d Antony Chazapis
        allow = set()
1250 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user):
1251 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 1)[0])
1252 a9b3f29d Antony Chazapis
        return sorted(allow)
1253 a9b3f29d Antony Chazapis
    
1254 a9b3f29d Antony Chazapis
    def _allowed_containers(self, user, account):
1255 a9b3f29d Antony Chazapis
        allow = set()
1256 0f9d752c Antony Chazapis
        for path in self.permissions.access_list_paths(user, account):
1257 a9b3f29d Antony Chazapis
            allow.add(path.split('/', 2)[1])
1258 a9b3f29d Antony Chazapis
        return sorted(allow)