Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 2d8e6f94

History | View | Annotate | Download (59.4 kB)

1 2e662088 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 d50ed8d4 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 d50ed8d4 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 d50ed8d4 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 d50ed8d4 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 d50ed8d4 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 d50ed8d4 Sofia Papagiannaki
47 d50ed8d4 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 4cfccdd2 Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
83 4cfccdd2 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 d50ed8d4 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 d50ed8d4 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 d50ed8d4 Sofia Papagiannaki

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