Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 90311d98

History | View | Annotate | Download (60.7 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 a9b3f29d Antony Chazapis
        try:
116 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
117 39ef6f41 Antony Chazapis
            for m in self.messages:
118 39ef6f41 Antony Chazapis
                self.queue.send(*m)
119 5578064f root
            if serials:
120 5578064f root
                self.quotaholder.accept_commission(
121 5578064f root
                            context     =   {},
122 5578064f root
                            clientkey   =   'pithos',
123 5578064f root
                            serials     =   serials)
124 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
125 a9b3f29d Antony Chazapis
            return ret
126 a9b3f29d Antony Chazapis
        except:
127 5578064f root
            self.quotaholder.reject_commission(
128 5578064f root
                        context     =   {},
129 5578064f root
                        clientkey   =   'pithos',
130 5578064f root
                        serials     =   serials)
131 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
132 a9b3f29d Antony Chazapis
            raise
133 a9b3f29d Antony Chazapis
    return fn
134 a9b3f29d Antony Chazapis
135 a9b3f29d Antony Chazapis
136 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
137 a9b3f29d Antony Chazapis
    """A modular backend.
138 2715ade4 Sofia Papagiannaki

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