Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (61.4 kB)

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

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