Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 0ed09c7c

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