Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 019d9e19

History | View | Annotate | Download (61.5 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 b3119c3e Georgios D. Tsoukalas
from kamaki.clients.quotaholder import QuotaholderClient
43 0307b47f Georgios D. Tsoukalas
44 7efc9f86 Sofia Papagiannaki
from base import DEFAULT_QUOTA, DEFAULT_VERSIONING, NotAllowedError, QuotaError, BaseBackend, \
45 7efc9f86 Sofia Papagiannaki
    AccountExists, ContainerExists, AccountNotEmpty, ContainerNotEmpty, ItemNotExists, VersionNotExists
46 a9b3f29d Antony Chazapis
47 6e147ecc Antony Chazapis
# Stripped-down version of the HashMap class found in tools.
48 2715ade4 Sofia Papagiannaki
49 2715ade4 Sofia Papagiannaki
50 6e147ecc Antony Chazapis
class HashMap(list):
51 6e147ecc Antony Chazapis
52 6e147ecc Antony Chazapis
    def __init__(self, blocksize, blockhash):
53 6e147ecc Antony Chazapis
        super(HashMap, self).__init__()
54 6e147ecc Antony Chazapis
        self.blocksize = blocksize
55 6e147ecc Antony Chazapis
        self.blockhash = blockhash
56 6e147ecc Antony Chazapis
57 6e147ecc Antony Chazapis
    def _hash_raw(self, v):
58 6e147ecc Antony Chazapis
        h = hashlib.new(self.blockhash)
59 6e147ecc Antony Chazapis
        h.update(v)
60 6e147ecc Antony Chazapis
        return h.digest()
61 6e147ecc Antony Chazapis
62 6e147ecc Antony Chazapis
    def hash(self):
63 6e147ecc Antony Chazapis
        if len(self) == 0:
64 6e147ecc Antony Chazapis
            return self._hash_raw('')
65 6e147ecc Antony Chazapis
        if len(self) == 1:
66 6e147ecc Antony Chazapis
            return self.__getitem__(0)
67 6e147ecc Antony Chazapis
68 6e147ecc Antony Chazapis
        h = list(self)
69 6e147ecc Antony Chazapis
        s = 2
70 6e147ecc Antony Chazapis
        while s < len(h):
71 6e147ecc Antony Chazapis
            s = s * 2
72 6e147ecc Antony Chazapis
        h += [('\x00' * len(h[0]))] * (s - len(h))
73 6e147ecc Antony Chazapis
        while len(h) > 1:
74 6e147ecc Antony Chazapis
            h = [self._hash_raw(h[x] + h[x + 1]) for x in range(0, len(h), 2)]
75 6e147ecc Antony Chazapis
        return h[0]
76 5a96180b Antony Chazapis
77 228de81b Antony Chazapis
# Default modules and settings.
78 228de81b Antony Chazapis
DEFAULT_DB_MODULE = 'pithos.backends.lib.sqlalchemy'
79 228de81b Antony Chazapis
DEFAULT_DB_CONNECTION = 'sqlite:///backend.db'
80 228de81b Antony Chazapis
DEFAULT_BLOCK_MODULE = 'pithos.backends.lib.hashfiler'
81 228de81b Antony Chazapis
DEFAULT_BLOCK_PATH = 'data/'
82 f3b65e8f Antony Chazapis
DEFAULT_BLOCK_UMASK = 0o022
83 fa9cae7e Antony Chazapis
#DEFAULT_QUEUE_MODULE = 'pithos.backends.lib.rabbitmq'
84 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
85 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
86 fa9cae7e Antony Chazapis
87 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
88 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
89 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
90 228de81b Antony Chazapis
91 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
92 44ad5860 Antony Chazapis
93 44ad5860 Antony Chazapis
inf = float('inf')
94 44ad5860 Antony Chazapis
95 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
96 bb4eafc6 Antony Chazapis
97 a9b3f29d Antony Chazapis
98 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
99 a9b3f29d Antony Chazapis
100 1c2fc0ff Antony Chazapis
101 a9b3f29d Antony Chazapis
def backend_method(func=None, autocommit=1):
102 a9b3f29d Antony Chazapis
    if func is None:
103 a9b3f29d Antony Chazapis
        def fn(func):
104 a9b3f29d Antony Chazapis
            return backend_method(func, autocommit)
105 a9b3f29d Antony Chazapis
        return fn
106 a9b3f29d Antony Chazapis
107 a9b3f29d Antony Chazapis
    if not autocommit:
108 a9b3f29d Antony Chazapis
        return func
109 2715ade4 Sofia Papagiannaki
110 a9b3f29d Antony Chazapis
    def fn(self, *args, **kw):
111 2c5363a0 Antony Chazapis
        self.wrapper.execute()
112 ecd01afd root
        serials = []
113 ecd01afd root
        self.serials = serials
114 85352a77 root
        self.messages = []
115 8313c681 root
116 a9b3f29d Antony Chazapis
        try:
117 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
118 39ef6f41 Antony Chazapis
            for m in self.messages:
119 39ef6f41 Antony Chazapis
                self.queue.send(*m)
120 5578064f root
            if serials:
121 5578064f root
                self.quotaholder.accept_commission(
122 5578064f root
                            context     =   {},
123 5578064f root
                            clientkey   =   'pithos',
124 5578064f root
                            serials     =   serials)
125 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
126 a9b3f29d Antony Chazapis
            return ret
127 a9b3f29d Antony Chazapis
        except:
128 73fbe301 Sofia Papagiannaki
            if serials:
129 73fbe301 Sofia Papagiannaki
                self.quotaholder.reject_commission(
130 73fbe301 Sofia Papagiannaki
                            context     =   {},
131 73fbe301 Sofia Papagiannaki
                            clientkey   =   'pithos',
132 73fbe301 Sofia Papagiannaki
                            serials     =   serials)
133 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
134 a9b3f29d Antony Chazapis
            raise
135 a9b3f29d Antony Chazapis
    return fn
136 a9b3f29d Antony Chazapis
137 a9b3f29d Antony Chazapis
138 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
139 a9b3f29d Antony Chazapis
    """A modular backend.
140 2715ade4 Sofia Papagiannaki

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