Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (58.8 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 2715ade4 Sofia Papagiannaki
#
3 a9b3f29d Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 a9b3f29d Antony Chazapis
# without modification, are permitted provided that the following
5 a9b3f29d Antony Chazapis
# conditions are met:
6 2715ade4 Sofia Papagiannaki
#
7 a9b3f29d Antony Chazapis
#   1. Redistributions of source code must retain the above
8 a9b3f29d Antony Chazapis
#      copyright notice, this list of conditions and the following
9 a9b3f29d Antony Chazapis
#      disclaimer.
10 2715ade4 Sofia Papagiannaki
#
11 7ff57991 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 7ff57991 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 a9b3f29d Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 a9b3f29d Antony Chazapis
#      provided with the distribution.
15 2715ade4 Sofia Papagiannaki
#
16 a9b3f29d Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 a9b3f29d Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 a9b3f29d Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 a9b3f29d Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 a9b3f29d Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 a9b3f29d Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 a9b3f29d Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 a9b3f29d Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 a9b3f29d Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 a9b3f29d Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 a9b3f29d Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 a9b3f29d Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 2715ade4 Sofia Papagiannaki
#
29 a9b3f29d Antony Chazapis
# The views and conclusions contained in the software and
30 a9b3f29d Antony Chazapis
# documentation are those of the authors and should not be
31 a9b3f29d Antony Chazapis
# interpreted as representing official policies, either expressed
32 a9b3f29d Antony Chazapis
# or implied, of GRNET S.A.
33 a9b3f29d Antony Chazapis
34 2c5363a0 Antony Chazapis
import sys
35 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 2715ade4 Sofia Papagiannaki
47 2715ade4 Sofia Papagiannaki
48 6e147ecc Antony Chazapis
class HashMap(list):
49 6e147ecc Antony Chazapis
50 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
51 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
52 6e147ecc Antony Chazapis
        self.blocksize = blocksize
53 6e147ecc Antony Chazapis
        self.blockhash = blockhash
54 6e147ecc Antony Chazapis
55 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
56 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
57 6e147ecc Antony Chazapis
        h.update(v)
58 6e147ecc Antony Chazapis
        return h.digest()
59 6e147ecc Antony Chazapis
60 6e147ecc Antony Chazapis
    def hash(self):
61 6e147ecc Antony Chazapis
        if len(self) == 0:
62 6e147ecc Antony Chazapis
            return self._hash_raw('')
63 6e147ecc Antony Chazapis
        if len(self) == 1:
64 6e147ecc Antony Chazapis
            return self.__getitem__(0)
65 6e147ecc Antony Chazapis
66 6e147ecc Antony Chazapis
        h = list(self)
67 6e147ecc Antony Chazapis
        s = 2
68 6e147ecc Antony Chazapis
        while s < len(h):
69 6e147ecc Antony Chazapis
            s = s * 2
70 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
71 6e147ecc Antony Chazapis
        while len(h) > 1:
72 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
73 6e147ecc Antony Chazapis
        return h[0]
74 5a96180b Antony Chazapis
75 228de81b Antony Chazapis
# Default modules and settings.
76 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
77 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
78 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
79 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
80 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
81 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
82 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
83 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
84 fa9cae7e Antony Chazapis
85 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
86 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
87 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
88 228de81b Antony Chazapis
89 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
90 44ad5860 Antony Chazapis
91 44ad5860 Antony Chazapis
inf = float('inf')
92 44ad5860 Antony Chazapis
93 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
94 bb4eafc6 Antony Chazapis
95 a9b3f29d Antony Chazapis
96 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
97 a9b3f29d Antony Chazapis
98 1c2fc0ff Antony Chazapis
99 a9b3f29d Antony Chazapis
def backend_method(func=None, autocommit=1):
100 a9b3f29d Antony Chazapis
    if func is None:
101 a9b3f29d Antony Chazapis
        def fn(func):
102 a9b3f29d Antony Chazapis
            return backend_method(func, autocommit)
103 a9b3f29d Antony Chazapis
        return fn
104 a9b3f29d Antony Chazapis
105 a9b3f29d Antony Chazapis
    if not autocommit:
106 a9b3f29d Antony Chazapis
        return func
107 2715ade4 Sofia Papagiannaki
108 a9b3f29d Antony Chazapis
    def fn(self, *args, **kw):
109 2c5363a0 Antony Chazapis
        self.wrapper.execute()
110 a9b3f29d Antony Chazapis
        try:
111 39ef6f41 Antony Chazapis
            self.messages = []
112 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
113 39ef6f41 Antony Chazapis
            for m in self.messages:
114 39ef6f41 Antony Chazapis
                self.queue.send(*m)
115 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
116 a9b3f29d Antony Chazapis
            return ret
117 a9b3f29d Antony Chazapis
        except:
118 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
119 a9b3f29d Antony Chazapis
            raise
120 a9b3f29d Antony Chazapis
    return fn
121 a9b3f29d Antony Chazapis
122 a9b3f29d Antony Chazapis
123 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
124 a9b3f29d Antony Chazapis
    """A modular backend.
125 2715ade4 Sofia Papagiannaki

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