Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-backend / pithos / backends / modular.py @ 4a105ce2

History | View | Annotate | Download (64 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 b336e6fa Georgios D. Tsoukalas
from synnefo.lib.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 7f1f0464 Georgios D. Tsoukalas
DEFAULT_BLOCK_PARAMS = { 'mappool': None, 'blockpool': None }
85 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_HOSTS = '[amqp://guest:guest@localhost:5672]'
86 f4fbb0fa Sofia Papagiannaki
#DEFAULT_QUEUE_EXCHANGE = 'pithos'
87 4a105ce2 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_ALPHABET = ('0123456789'
88 4a105ce2 Sofia Papagiannaki
                               'abcdefghijklmnopqrstuvwxyz'
89 4a105ce2 Sofia Papagiannaki
                               'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
90 4a105ce2 Sofia Papagiannaki
DEFAULT_PUBLIC_URL_SECURITY = 8
91 fa9cae7e Antony Chazapis
92 8d9a3fbd Antony Chazapis
QUEUE_MESSAGE_KEY_PREFIX = 'pithos.%s'
93 39ef6f41 Antony Chazapis
QUEUE_CLIENT_ID = 'pithos'
94 73673127 Antony Chazapis
QUEUE_INSTANCE_ID = '1'
95 228de81b Antony Chazapis
96 2715ade4 Sofia Papagiannaki
(CLUSTER_NORMAL, CLUSTER_HISTORY, CLUSTER_DELETED) = range(3)
97 44ad5860 Antony Chazapis
98 44ad5860 Antony Chazapis
inf = float('inf')
99 44ad5860 Antony Chazapis
100 bb4eafc6 Antony Chazapis
ULTIMATE_ANSWER = 42
101 bb4eafc6 Antony Chazapis
102 a9b3f29d Antony Chazapis
103 a9b3f29d Antony Chazapis
logger = logging.getLogger(__name__)
104 a9b3f29d Antony Chazapis
105 1c2fc0ff Antony Chazapis
106 a9b3f29d Antony Chazapis
def backend_method(func=None, autocommit=1):
107 a9b3f29d Antony Chazapis
    if func is None:
108 a9b3f29d Antony Chazapis
        def fn(func):
109 a9b3f29d Antony Chazapis
            return backend_method(func, autocommit)
110 a9b3f29d Antony Chazapis
        return fn
111 a9b3f29d Antony Chazapis
112 a9b3f29d Antony Chazapis
    if not autocommit:
113 a9b3f29d Antony Chazapis
        return func
114 2715ade4 Sofia Papagiannaki
115 a9b3f29d Antony Chazapis
    def fn(self, *args, **kw):
116 2c5363a0 Antony Chazapis
        self.wrapper.execute()
117 ecd01afd root
        serials = []
118 ecd01afd root
        self.serials = serials
119 85352a77 root
        self.messages = []
120 8313c681 root
121 a9b3f29d Antony Chazapis
        try:
122 a9b3f29d Antony Chazapis
            ret = func(self, *args, **kw)
123 39ef6f41 Antony Chazapis
            for m in self.messages:
124 39ef6f41 Antony Chazapis
                self.queue.send(*m)
125 5578064f root
            if serials:
126 5578064f root
                self.quotaholder.accept_commission(
127 5578064f root
                            context     =   {},
128 5578064f root
                            clientkey   =   'pithos',
129 5578064f root
                            serials     =   serials)
130 a74ba506 Sofia Papagiannaki
            self.wrapper.commit()
131 a9b3f29d Antony Chazapis
            return ret
132 a9b3f29d Antony Chazapis
        except:
133 73fbe301 Sofia Papagiannaki
            if serials:
134 73fbe301 Sofia Papagiannaki
                self.quotaholder.reject_commission(
135 73fbe301 Sofia Papagiannaki
                            context     =   {},
136 73fbe301 Sofia Papagiannaki
                            clientkey   =   'pithos',
137 73fbe301 Sofia Papagiannaki
                            serials     =   serials)
138 2c5363a0 Antony Chazapis
            self.wrapper.rollback()
139 a9b3f29d Antony Chazapis
            raise
140 a9b3f29d Antony Chazapis
    return fn
141 a9b3f29d Antony Chazapis
142 a9b3f29d Antony Chazapis
143 a9b3f29d Antony Chazapis
class ModularBackend(BaseBackend):
144 a9b3f29d Antony Chazapis
    """A modular backend.
145 2715ade4 Sofia Papagiannaki

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