Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / plankton / backend.py @ 7856a37a

History | View | Annotate | Download (18.2 kB)

1 c34de90f Giorgos Verigakis
# Copyright 2011 GRNET S.A. All rights reserved.
2 c34de90f Giorgos Verigakis
#
3 c34de90f Giorgos Verigakis
# Redistribution and use in source and binary forms, with or
4 c34de90f Giorgos Verigakis
# without modification, are permitted provided that the following
5 c34de90f Giorgos Verigakis
# conditions are met:
6 c34de90f Giorgos Verigakis
#
7 c34de90f Giorgos Verigakis
#   1. Redistributions of source code must retain the above
8 c34de90f Giorgos Verigakis
#      copyright notice, this list of conditions and the following
9 c34de90f Giorgos Verigakis
#      disclaimer.
10 c34de90f Giorgos Verigakis
#
11 c34de90f Giorgos Verigakis
#   2. Redistributions in binary form must reproduce the above
12 c34de90f Giorgos Verigakis
#      copyright notice, this list of conditions and the following
13 c34de90f Giorgos Verigakis
#      disclaimer in the documentation and/or other materials
14 c34de90f Giorgos Verigakis
#      provided with the distribution.
15 c34de90f Giorgos Verigakis
#
16 c34de90f Giorgos Verigakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 c34de90f Giorgos Verigakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 c34de90f Giorgos Verigakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 c34de90f Giorgos Verigakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 c34de90f Giorgos Verigakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 c34de90f Giorgos Verigakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 c34de90f Giorgos Verigakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 c34de90f Giorgos Verigakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 c34de90f Giorgos Verigakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 c34de90f Giorgos Verigakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 c34de90f Giorgos Verigakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 c34de90f Giorgos Verigakis
# POSSIBILITY OF SUCH DAMAGE.
28 c34de90f Giorgos Verigakis
#
29 c34de90f Giorgos Verigakis
# The views and conclusions contained in the software and
30 c34de90f Giorgos Verigakis
# documentation are those of the authors and should not be
31 c34de90f Giorgos Verigakis
# interpreted as representing official policies, either expressed
32 c34de90f Giorgos Verigakis
# or implied, of GRNET S.A.
33 c34de90f Giorgos Verigakis
34 f5afd99b Giorgos Verigakis
"""
35 bfd9f988 Giorgos Verigakis
The Plankton attributes are the following:
36 0a72907b Giorgos Verigakis
  - checksum: the 'hash' meta
37 7bd1d3b5 Giorgos Verigakis
  - container_format: stored as a user meta
38 0a72907b Giorgos Verigakis
  - created_at: the 'modified' meta of the first version
39 0a72907b Giorgos Verigakis
  - deleted_at: the timestamp of the last version
40 7bd1d3b5 Giorgos Verigakis
  - disk_format: stored as a user meta
41 0a72907b Giorgos Verigakis
  - id: the 'uuid' meta
42 f5afd99b Giorgos Verigakis
  - is_public: True if there is a * entry for the read permission
43 0a72907b Giorgos Verigakis
  - location: generated based on the file's path
44 7bd1d3b5 Giorgos Verigakis
  - name: stored as a user meta
45 0a72907b Giorgos Verigakis
  - owner: the file's account
46 7bd1d3b5 Giorgos Verigakis
  - properties: stored as user meta prefixed with PROPERTY_PREFIX
47 0a72907b Giorgos Verigakis
  - size: the 'bytes' meta
48 7bd1d3b5 Giorgos Verigakis
  - status: stored as a system meta
49 7bd1d3b5 Giorgos Verigakis
  - store: is always 'pithos'
50 0a72907b Giorgos Verigakis
  - updated_at: the 'modified' meta
51 f5afd99b Giorgos Verigakis
"""
52 f5afd99b Giorgos Verigakis
53 c34de90f Giorgos Verigakis
import json
54 c23d211a Giorgos Verigakis
import warnings
55 c34de90f Giorgos Verigakis
56 c34de90f Giorgos Verigakis
from operator import itemgetter
57 2db7d9df Christos Stavrakakis
from time import gmtime, strftime
58 469d0997 Georgios D. Tsoukalas
from functools import wraps, partial
59 c34de90f Giorgos Verigakis
60 c34de90f Giorgos Verigakis
from django.conf import settings
61 c34de90f Giorgos Verigakis
62 2db7d9df Christos Stavrakakis
from pithos.backends.base import NotAllowedError as PithosNotAllowedError
63 469d0997 Georgios D. Tsoukalas
import synnefo.lib.astakos as lib_astakos
64 469d0997 Georgios D. Tsoukalas
import logging
65 469d0997 Georgios D. Tsoukalas
66 f4366b6c Stratos Psomadakis
from synnefo.settings import (CYCLADES_USE_QUOTAHOLDER,
67 f4366b6c Stratos Psomadakis
                              CYCLADES_QUOTAHOLDER_URL,
68 f4366b6c Stratos Psomadakis
                              CYCLADES_QUOTAHOLDER_TOKEN)
69 f4366b6c Stratos Psomadakis
70 469d0997 Georgios D. Tsoukalas
logger = logging.getLogger(__name__)
71 c34de90f Giorgos Verigakis
72 c34de90f Giorgos Verigakis
73 0a72907b Giorgos Verigakis
PLANKTON_DOMAIN = 'plankton'
74 bfd9f988 Giorgos Verigakis
PLANKTON_PREFIX = 'plankton:'
75 7bd1d3b5 Giorgos Verigakis
PROPERTY_PREFIX = 'property:'
76 7bd1d3b5 Giorgos Verigakis
77 bfd9f988 Giorgos Verigakis
PLANKTON_META = ('container_format', 'disk_format', 'name', 'properties',
78 bfd9f988 Giorgos Verigakis
                 'status')
79 c34de90f Giorgos Verigakis
80 469d0997 Georgios D. Tsoukalas
TRANSLATE_UUIDS = getattr(settings, 'TRANSLATE_UUIDS', False)
81 469d0997 Georgios D. Tsoukalas
82 7856a37a Stratos Psomadakis
83 469d0997 Georgios D. Tsoukalas
def get_displaynames(names):
84 469d0997 Georgios D. Tsoukalas
    try:
85 469d0997 Georgios D. Tsoukalas
        auth_url = settings.ASTAKOS_URL
86 469d0997 Georgios D. Tsoukalas
        url = auth_url.replace('im/authenticate', 'service/api/user_catalogs')
87 469d0997 Georgios D. Tsoukalas
        token = settings.CYCLADES_ASTAKOS_SERVICE_TOKEN
88 469d0997 Georgios D. Tsoukalas
        uuids = lib_astakos.get_displaynames(token, names, url=url)
89 469d0997 Georgios D. Tsoukalas
    except Exception, e:
90 469d0997 Georgios D. Tsoukalas
        logger.exception(e)
91 469d0997 Georgios D. Tsoukalas
        return {}
92 469d0997 Georgios D. Tsoukalas
93 469d0997 Georgios D. Tsoukalas
    return uuids
94 469d0997 Georgios D. Tsoukalas
95 c34de90f Giorgos Verigakis
96 7bd1d3b5 Giorgos Verigakis
def get_location(account, container, object):
97 7bd1d3b5 Giorgos Verigakis
    assert '/' not in account, "Invalid account"
98 7bd1d3b5 Giorgos Verigakis
    assert '/' not in container, "Invalid container"
99 7bd1d3b5 Giorgos Verigakis
    return 'pithos://%s/%s/%s' % (account, container, object)
100 7bd1d3b5 Giorgos Verigakis
101 7784ab88 Christos Stavrakakis
102 7bd1d3b5 Giorgos Verigakis
def split_location(location):
103 7bd1d3b5 Giorgos Verigakis
    """Returns (accout, container, object) from a location string"""
104 7bd1d3b5 Giorgos Verigakis
    t = location.split('/', 4)
105 7bd1d3b5 Giorgos Verigakis
    assert len(t) == 5, "Invalid location"
106 7bd1d3b5 Giorgos Verigakis
    return t[2:5]
107 7bd1d3b5 Giorgos Verigakis
108 7bd1d3b5 Giorgos Verigakis
109 0a72907b Giorgos Verigakis
class BackendException(Exception):
110 0a72907b Giorgos Verigakis
    pass
111 c34de90f Giorgos Verigakis
112 c34de90f Giorgos Verigakis
113 2db7d9df Christos Stavrakakis
class NotAllowedError(BackendException):
114 2db7d9df Christos Stavrakakis
    pass
115 2db7d9df Christos Stavrakakis
116 2db7d9df Christos Stavrakakis
117 7784ab88 Christos Stavrakakis
from pithos.backends.util import PithosBackendPool
118 7784ab88 Christos Stavrakakis
POOL_SIZE = 8
119 7784ab88 Christos Stavrakakis
_pithos_backend_pool = \
120 cc92b70f Christos Stavrakakis
    PithosBackendPool(POOL_SIZE,
121 f4366b6c Stratos Psomadakis
                      quotaholder_enabled=CYCLADES_USE_QUOTAHOLDER,
122 f4366b6c Stratos Psomadakis
                      quotaholder_url=CYCLADES_QUOTAHOLDER_URL,
123 f4366b6c Stratos Psomadakis
                      quotaholder_token=CYCLADES_QUOTAHOLDER_TOKEN,
124 cc92b70f Christos Stavrakakis
                      db_connection=settings.BACKEND_DB_CONNECTION,
125 cc92b70f Christos Stavrakakis
                      block_path=settings.BACKEND_BLOCK_PATH)
126 7784ab88 Christos Stavrakakis
127 7784ab88 Christos Stavrakakis
128 7784ab88 Christos Stavrakakis
def get_pithos_backend():
129 7784ab88 Christos Stavrakakis
    return _pithos_backend_pool.pool_get()
130 7784ab88 Christos Stavrakakis
131 7784ab88 Christos Stavrakakis
132 2db7d9df Christos Stavrakakis
def handle_backend_exceptions(func):
133 2db7d9df Christos Stavrakakis
    @wraps(func)
134 2db7d9df Christos Stavrakakis
    def wrapper(*args, **kwargs):
135 2db7d9df Christos Stavrakakis
        try:
136 2db7d9df Christos Stavrakakis
            return func(*args, **kwargs)
137 2db7d9df Christos Stavrakakis
        except PithosNotAllowedError:
138 2db7d9df Christos Stavrakakis
            raise NotAllowedError()
139 2db7d9df Christos Stavrakakis
    return wrapper
140 2db7d9df Christos Stavrakakis
141 2db7d9df Christos Stavrakakis
142 ffab341c Christos Stavrakakis
class ImageBackend(object):
143 f5afd99b Giorgos Verigakis
    """A wrapper arround the pithos backend to simplify image handling."""
144 1e28ba40 Christos Stavrakakis
145 c34de90f Giorgos Verigakis
    def __init__(self, user):
146 c34de90f Giorgos Verigakis
        self.user = user
147 7784ab88 Christos Stavrakakis
148 c23d211a Giorgos Verigakis
        original_filters = warnings.filters
149 c23d211a Giorgos Verigakis
        warnings.simplefilter('ignore')         # Suppress SQLAlchemy warnings
150 7784ab88 Christos Stavrakakis
        self.backend = get_pithos_backend()
151 c23d211a Giorgos Verigakis
        warnings.filters = original_filters     # Restore warnings
152 7784ab88 Christos Stavrakakis
153 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
154 7bd1d3b5 Giorgos Verigakis
    def _get_image(self, location):
155 f5afd99b Giorgos Verigakis
        def format_timestamp(t):
156 f5afd99b Giorgos Verigakis
            return strftime('%Y-%m-%d %H:%M:%S', gmtime(t))
157 1e28ba40 Christos Stavrakakis
158 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
159 1e28ba40 Christos Stavrakakis
160 7bd1d3b5 Giorgos Verigakis
        try:
161 7bd1d3b5 Giorgos Verigakis
            versions = self.backend.list_versions(self.user, account,
162 cc92b70f Christos Stavrakakis
                                                  container, object)
163 7bd1d3b5 Giorgos Verigakis
        except NameError:
164 7bd1d3b5 Giorgos Verigakis
            return None
165 1e28ba40 Christos Stavrakakis
166 7bd1d3b5 Giorgos Verigakis
        image = {}
167 1e28ba40 Christos Stavrakakis
168 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
169 7bd1d3b5 Giorgos Verigakis
        if meta:
170 7bd1d3b5 Giorgos Verigakis
            image['deleted_at'] = ''
171 7bd1d3b5 Giorgos Verigakis
        else:
172 7bd1d3b5 Giorgos Verigakis
            # Object was deleted, use the latest version
173 7bd1d3b5 Giorgos Verigakis
            version, timestamp = versions[-1]
174 7bd1d3b5 Giorgos Verigakis
            meta = self._get_meta(location, version)
175 7bd1d3b5 Giorgos Verigakis
            image['deleted_at'] = format_timestamp(timestamp)
176 1e28ba40 Christos Stavrakakis
177 0a72907b Giorgos Verigakis
        if PLANKTON_PREFIX + 'name' not in meta:
178 0a72907b Giorgos Verigakis
            return None     # Not a Plankton image
179 1e28ba40 Christos Stavrakakis
180 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
181 1e28ba40 Christos Stavrakakis
182 0a72907b Giorgos Verigakis
        image['checksum'] = meta['hash']
183 7bd1d3b5 Giorgos Verigakis
        image['created_at'] = format_timestamp(versions[0][1])
184 0a72907b Giorgos Verigakis
        image['id'] = meta['uuid']
185 f5afd99b Giorgos Verigakis
        image['is_public'] = '*' in permissions.get('read', [])
186 7bd1d3b5 Giorgos Verigakis
        image['location'] = location
187 469d0997 Georgios D. Tsoukalas
        if TRANSLATE_UUIDS:
188 469d0997 Georgios D. Tsoukalas
            displaynames = get_displaynames([account])
189 469d0997 Georgios D. Tsoukalas
            if account in displaynames:
190 469d0997 Georgios D. Tsoukalas
                display_account = displaynames[account]
191 469d0997 Georgios D. Tsoukalas
            else:
192 469d0997 Georgios D. Tsoukalas
                display_account = 'unknown'
193 469d0997 Georgios D. Tsoukalas
            image['owner'] = display_account
194 469d0997 Georgios D. Tsoukalas
        else:
195 469d0997 Georgios D. Tsoukalas
            image['owner'] = account
196 0a72907b Giorgos Verigakis
        image['size'] = meta['bytes']
197 7bd1d3b5 Giorgos Verigakis
        image['store'] = 'pithos'
198 0a72907b Giorgos Verigakis
        image['updated_at'] = format_timestamp(meta['modified'])
199 7bd1d3b5 Giorgos Verigakis
        image['properties'] = {}
200 1e28ba40 Christos Stavrakakis
201 7bd1d3b5 Giorgos Verigakis
        for key, val in meta.items():
202 bfd9f988 Giorgos Verigakis
            if not key.startswith(PLANKTON_PREFIX):
203 bfd9f988 Giorgos Verigakis
                continue
204 bfd9f988 Giorgos Verigakis
            key = key[len(PLANKTON_PREFIX):]
205 bfd9f988 Giorgos Verigakis
            if key == 'properties':
206 bfd9f988 Giorgos Verigakis
                val = json.loads(val)
207 bfd9f988 Giorgos Verigakis
            if key in PLANKTON_META:
208 7bd1d3b5 Giorgos Verigakis
                image[key] = val
209 1e28ba40 Christos Stavrakakis
210 0a72907b Giorgos Verigakis
        return image
211 1e28ba40 Christos Stavrakakis
212 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
213 bfd9f988 Giorgos Verigakis
    def _get_meta(self, location, version=None):
214 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
215 7bd1d3b5 Giorgos Verigakis
        try:
216 bfd9f988 Giorgos Verigakis
            return self.backend.get_object_meta(self.user, account, container,
217 cc92b70f Christos Stavrakakis
                                                object, PLANKTON_DOMAIN,
218 cc92b70f Christos Stavrakakis
                                                version)
219 7bd1d3b5 Giorgos Verigakis
        except NameError:
220 7bd1d3b5 Giorgos Verigakis
            return None
221 1e28ba40 Christos Stavrakakis
222 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
223 7bd1d3b5 Giorgos Verigakis
    def _get_permissions(self, location):
224 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
225 cc92b70f Christos Stavrakakis
        _a, _p, permissions = self.backend.get_object_permissions(self.user,
226 cc92b70f Christos Stavrakakis
                                                                  account,
227 cc92b70f Christos Stavrakakis
                                                                  container,
228 cc92b70f Christos Stavrakakis
                                                                  object)
229 f5afd99b Giorgos Verigakis
        return permissions
230 1e28ba40 Christos Stavrakakis
231 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
232 c23d211a Giorgos Verigakis
    def _store(self, f, size=None):
233 7bd1d3b5 Giorgos Verigakis
        """Breaks data into blocks and stores them in the backend"""
234 1e28ba40 Christos Stavrakakis
235 c23d211a Giorgos Verigakis
        bytes = 0
236 f5afd99b Giorgos Verigakis
        hashmap = []
237 7bd1d3b5 Giorgos Verigakis
        backend = self.backend
238 c23d211a Giorgos Verigakis
        blocksize = backend.block_size
239 1e28ba40 Christos Stavrakakis
240 c23d211a Giorgos Verigakis
        data = f.read(blocksize)
241 7bd1d3b5 Giorgos Verigakis
        while data:
242 c23d211a Giorgos Verigakis
            hash = backend.put_block(data)
243 f5afd99b Giorgos Verigakis
            hashmap.append(hash)
244 c23d211a Giorgos Verigakis
            bytes += len(data)
245 c23d211a Giorgos Verigakis
            data = f.read(blocksize)
246 1e28ba40 Christos Stavrakakis
247 c23d211a Giorgos Verigakis
        if size and size != bytes:
248 c23d211a Giorgos Verigakis
            raise BackendException("Invalid size")
249 1e28ba40 Christos Stavrakakis
250 c23d211a Giorgos Verigakis
        return hashmap, bytes
251 1e28ba40 Christos Stavrakakis
252 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
253 7bd1d3b5 Giorgos Verigakis
    def _update(self, location, size, hashmap, meta, permissions):
254 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
255 7bd1d3b5 Giorgos Verigakis
        self.backend.update_object_hashmap(self.user, account, container,
256 cc92b70f Christos Stavrakakis
                                           object, size, hashmap, '',
257 cc92b70f Christos Stavrakakis
                                           PLANKTON_DOMAIN,
258 cc92b70f Christos Stavrakakis
                                           permissions=permissions)
259 0a72907b Giorgos Verigakis
        self._update_meta(location, meta, replace=True)
260 1e28ba40 Christos Stavrakakis
261 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
262 0a72907b Giorgos Verigakis
    def _update_meta(self, location, meta, replace=False):
263 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
264 1e28ba40 Christos Stavrakakis
265 bfd9f988 Giorgos Verigakis
        prefixed = {}
266 0a72907b Giorgos Verigakis
        for key, val in meta.items():
267 bfd9f988 Giorgos Verigakis
            if key == 'properties':
268 bfd9f988 Giorgos Verigakis
                val = json.dumps(val)
269 bfd9f988 Giorgos Verigakis
            if key in PLANKTON_META:
270 bfd9f988 Giorgos Verigakis
                prefixed[PLANKTON_PREFIX + key] = val
271 1e28ba40 Christos Stavrakakis
272 bfd9f988 Giorgos Verigakis
        self.backend.update_object_meta(self.user, account, container, object,
273 cc92b70f Christos Stavrakakis
                                        PLANKTON_DOMAIN, prefixed, replace)
274 1e28ba40 Christos Stavrakakis
275 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
276 7bd1d3b5 Giorgos Verigakis
    def _update_permissions(self, location, permissions):
277 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
278 7bd1d3b5 Giorgos Verigakis
        self.backend.update_object_permissions(self.user, account, container,
279 cc92b70f Christos Stavrakakis
                                               object, permissions)
280 1e28ba40 Christos Stavrakakis
281 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
282 f5afd99b Giorgos Verigakis
    def add_user(self, image_id, user):
283 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
284 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
285 1e28ba40 Christos Stavrakakis
286 7bd1d3b5 Giorgos Verigakis
        location = image['location']
287 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
288 f5afd99b Giorgos Verigakis
        read = set(permissions.get('read', []))
289 f5afd99b Giorgos Verigakis
        read.add(user)
290 f5afd99b Giorgos Verigakis
        permissions['read'] = list(read)
291 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
292 1e28ba40 Christos Stavrakakis
293 f5afd99b Giorgos Verigakis
    def close(self):
294 f5afd99b Giorgos Verigakis
        self.backend.close()
295 1e28ba40 Christos Stavrakakis
296 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
297 6ef51e9f Giorgos Verigakis
    def delete(self, image_id):
298 6ef51e9f Giorgos Verigakis
        image = self.get_image(image_id)
299 6ef51e9f Giorgos Verigakis
        account, container, object = split_location(image['location'])
300 6ef51e9f Giorgos Verigakis
        self.backend.delete_object(self.user, account, container, object)
301 1e28ba40 Christos Stavrakakis
302 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
303 7bd1d3b5 Giorgos Verigakis
    def get_data(self, location):
304 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
305 7bd1d3b5 Giorgos Verigakis
        size, hashmap = self.backend.get_object_hashmap(self.user, account,
306 cc92b70f Christos Stavrakakis
                                                        container, object)
307 f5afd99b Giorgos Verigakis
        data = ''.join(self.backend.get_block(hash) for hash in hashmap)
308 f5afd99b Giorgos Verigakis
        assert len(data) == size
309 f5afd99b Giorgos Verigakis
        return data
310 1e28ba40 Christos Stavrakakis
311 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
312 bfd9f988 Giorgos Verigakis
    def get_image(self, image_id):
313 0a72907b Giorgos Verigakis
        try:
314 0a72907b Giorgos Verigakis
            account, container, object = self.backend.get_uuid(self.user,
315 cc92b70f Christos Stavrakakis
                                                               image_id)
316 0a72907b Giorgos Verigakis
        except NameError:
317 0a72907b Giorgos Verigakis
            return None
318 1e28ba40 Christos Stavrakakis
319 0a72907b Giorgos Verigakis
        location = get_location(account, container, object)
320 0a72907b Giorgos Verigakis
        return self._get_image(location)
321 1e28ba40 Christos Stavrakakis
322 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
323 aed77afe Christos Stavrakakis
    def _iter(self, public=False, filters=None, shared_from=None):
324 d19e8f77 Giorgos Verigakis
        filters = filters or {}
325 1e28ba40 Christos Stavrakakis
326 aed77afe Christos Stavrakakis
        # Fix keys
327 bfd9f988 Giorgos Verigakis
        keys = [PLANKTON_PREFIX + 'name']
328 434d1e68 Giorgos Verigakis
        size_range = (None, None)
329 f5afd99b Giorgos Verigakis
        for key, val in filters.items():
330 0a72907b Giorgos Verigakis
            if key == 'size_min':
331 c3bcaeff Stratos Psomadakis
                size_range = (val, size_range[1])
332 0a72907b Giorgos Verigakis
            elif key == 'size_max':
333 c3bcaeff Stratos Psomadakis
                size_range = (size_range[0], val)
334 f5afd99b Giorgos Verigakis
            else:
335 434d1e68 Giorgos Verigakis
                keys.append('%s = %s' % (PLANKTON_PREFIX + key, val))
336 1e28ba40 Christos Stavrakakis
337 aed77afe Christos Stavrakakis
        backend = self.backend
338 aed77afe Christos Stavrakakis
        if shared_from:
339 aed77afe Christos Stavrakakis
            # To get shared images, we connect as shared_from member and
340 aed77afe Christos Stavrakakis
            # get the list shared by us
341 aed77afe Christos Stavrakakis
            user = shared_from
342 aed77afe Christos Stavrakakis
        else:
343 aed77afe Christos Stavrakakis
            user = None if public else self.user
344 aed77afe Christos Stavrakakis
            accounts = backend.list_accounts(user)
345 aed77afe Christos Stavrakakis
346 aed77afe Christos Stavrakakis
        for account in accounts:
347 aed77afe Christos Stavrakakis
            for container in backend.list_containers(user, account,
348 0a72907b Giorgos Verigakis
                                                     shared=True):
349 aed77afe Christos Stavrakakis
                for path, _ in backend.list_objects(user, account, container,
350 aed77afe Christos Stavrakakis
                                                    domain=PLANKTON_DOMAIN,
351 aed77afe Christos Stavrakakis
                                                    keys=keys, shared=True,
352 aed77afe Christos Stavrakakis
                                                    size_range=size_range):
353 0a72907b Giorgos Verigakis
                    location = get_location(account, container, path)
354 0a72907b Giorgos Verigakis
                    image = self._get_image(location)
355 0a72907b Giorgos Verigakis
                    if image:
356 0a72907b Giorgos Verigakis
                        yield image
357 1e28ba40 Christos Stavrakakis
358 aed77afe Christos Stavrakakis
    def iter(self, filters=None):
359 aed77afe Christos Stavrakakis
        """Iter over all images available to the user"""
360 aed77afe Christos Stavrakakis
        return self._iter(filters=filters)
361 1e28ba40 Christos Stavrakakis
362 aed77afe Christos Stavrakakis
    def iter_public(self, filters=None):
363 aed77afe Christos Stavrakakis
        """Iter over public images"""
364 aed77afe Christos Stavrakakis
        return self._iter(public=True, filters=filters)
365 1e28ba40 Christos Stavrakakis
366 aed77afe Christos Stavrakakis
    def iter_shared(self, filters=None, member=None):
367 aed77afe Christos Stavrakakis
        """Iter over images shared to member"""
368 d0e8984d Christos Stavrakakis
        return self._iter(filters=filters, shared_from=member)
369 1e28ba40 Christos Stavrakakis
370 aed77afe Christos Stavrakakis
    def list(self, filters=None, params={}):
371 aed77afe Christos Stavrakakis
        """Return all images available to the user"""
372 aed77afe Christos Stavrakakis
        images = list(self.iter(filters))
373 aed77afe Christos Stavrakakis
        key = itemgetter(params.get('sort_key', 'created_at'))
374 aed77afe Christos Stavrakakis
        reverse = params.get('sort_dir', 'desc') == 'desc'
375 aed77afe Christos Stavrakakis
        images.sort(key=key, reverse=reverse)
376 aed77afe Christos Stavrakakis
        return images
377 1e28ba40 Christos Stavrakakis
378 aed77afe Christos Stavrakakis
    def list_public(self, filters, params={}):
379 aed77afe Christos Stavrakakis
        """Return public images"""
380 f5afd99b Giorgos Verigakis
        images = list(self.iter_public(filters))
381 7bd1d3b5 Giorgos Verigakis
        key = itemgetter(params.get('sort_key', 'created_at'))
382 7bd1d3b5 Giorgos Verigakis
        reverse = params.get('sort_dir', 'desc') == 'desc'
383 f5afd99b Giorgos Verigakis
        images.sort(key=key, reverse=reverse)
384 f5afd99b Giorgos Verigakis
        return images
385 1e28ba40 Christos Stavrakakis
386 f5afd99b Giorgos Verigakis
    def list_users(self, image_id):
387 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
388 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
389 1e28ba40 Christos Stavrakakis
390 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(image['location'])
391 f5afd99b Giorgos Verigakis
        return [user for user in permissions.get('read', []) if user != '*']
392 1e28ba40 Christos Stavrakakis
393 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
394 c23d211a Giorgos Verigakis
    def put(self, name, f, params):
395 7bd1d3b5 Giorgos Verigakis
        assert 'checksum' not in params, "Passing a checksum is not supported"
396 7bd1d3b5 Giorgos Verigakis
        assert 'id' not in params, "Passing an ID is not supported"
397 7bd1d3b5 Giorgos Verigakis
        assert params.pop('store', 'pithos') == 'pithos', "Invalid store"
398 cc92b70f Christos Stavrakakis
        disk_format = params.setdefault('disk_format',
399 cc92b70f Christos Stavrakakis
                                        settings.DEFAULT_DISK_FORMAT)
400 cc92b70f Christos Stavrakakis
        assert disk_format in settings.ALLOWED_DISK_FORMATS,\
401 cc92b70f Christos Stavrakakis
            "Invalid disk_format"
402 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('container_format',
403 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_CONTAINER_FORMAT) in \
404 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
405 1e28ba40 Christos Stavrakakis
406 bfd9f988 Giorgos Verigakis
        container = settings.DEFAULT_PLANKTON_CONTAINER
407 c23d211a Giorgos Verigakis
        filename = params.pop('filename', name)
408 bfd9f988 Giorgos Verigakis
        location = 'pithos://%s/%s/%s' % (self.user, container, filename)
409 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
410 921355f8 Giorgos Verigakis
        permissions = {'read': ['*']} if is_public else {}
411 c23d211a Giorgos Verigakis
        size = params.pop('size', None)
412 1e28ba40 Christos Stavrakakis
413 c23d211a Giorgos Verigakis
        hashmap, size = self._store(f, size)
414 1e28ba40 Christos Stavrakakis
415 7bd1d3b5 Giorgos Verigakis
        meta = {}
416 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
417 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
418 1e28ba40 Christos Stavrakakis
419 7bd1d3b5 Giorgos Verigakis
        self._update(location, size, hashmap, meta, permissions)
420 0a72907b Giorgos Verigakis
        return self._get_image(location)
421 1e28ba40 Christos Stavrakakis
422 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
423 f5afd99b Giorgos Verigakis
    def register(self, name, location, params):
424 7bd1d3b5 Giorgos Verigakis
        assert 'id' not in params, "Passing an ID is not supported"
425 7bd1d3b5 Giorgos Verigakis
        assert location.startswith('pithos://'), "Invalid location"
426 7bd1d3b5 Giorgos Verigakis
        assert params.pop('store', 'pithos') == 'pithos', "Invalid store"
427 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('disk_format',
428 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_DISK_FORMAT) in \
429 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_DISK_FORMATS, "Invalid disk_format"
430 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('container_format',
431 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_CONTAINER_FORMAT) in \
432 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
433 1e28ba40 Christos Stavrakakis
434 1e28ba40 Christos Stavrakakis
        # user = self.user
435 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
436 1e28ba40 Christos Stavrakakis
437 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
438 06c8fb11 Giorgos Verigakis
        assert meta, "File not found"
439 1e28ba40 Christos Stavrakakis
440 bfd9f988 Giorgos Verigakis
        size = int(params.pop('size', meta['bytes']))
441 0a72907b Giorgos Verigakis
        if size != meta['bytes']:
442 c34de90f Giorgos Verigakis
            raise BackendException("Invalid size")
443 1e28ba40 Christos Stavrakakis
444 0a72907b Giorgos Verigakis
        checksum = params.pop('checksum', meta['hash'])
445 0a72907b Giorgos Verigakis
        if checksum != meta['hash']:
446 c34de90f Giorgos Verigakis
            raise BackendException("Invalid checksum")
447 1e28ba40 Christos Stavrakakis
448 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
449 aed77afe Christos Stavrakakis
        if is_public:
450 aed77afe Christos Stavrakakis
            permissions = {'read': ['*']}
451 aed77afe Christos Stavrakakis
        else:
452 aed77afe Christos Stavrakakis
            permissions = {'read': [self.user]}
453 1e28ba40 Christos Stavrakakis
454 f5afd99b Giorgos Verigakis
        meta = {}
455 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
456 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
457 1e28ba40 Christos Stavrakakis
458 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
459 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
460 0a72907b Giorgos Verigakis
        return self._get_image(location)
461 1e28ba40 Christos Stavrakakis
462 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
463 f5afd99b Giorgos Verigakis
    def remove_user(self, image_id, user):
464 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
465 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
466 1e28ba40 Christos Stavrakakis
467 7bd1d3b5 Giorgos Verigakis
        location = image['location']
468 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
469 f5afd99b Giorgos Verigakis
        try:
470 f5afd99b Giorgos Verigakis
            permissions.get('read', []).remove(user)
471 f5afd99b Giorgos Verigakis
        except ValueError:
472 f5afd99b Giorgos Verigakis
            return      # User did not have access anyway
473 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
474 1e28ba40 Christos Stavrakakis
475 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
476 f5afd99b Giorgos Verigakis
    def replace_users(self, image_id, users):
477 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
478 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
479 1e28ba40 Christos Stavrakakis
480 7bd1d3b5 Giorgos Verigakis
        location = image['location']
481 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
482 f5afd99b Giorgos Verigakis
        permissions['read'] = users
483 f5afd99b Giorgos Verigakis
        if image.get('is_public', False):
484 f5afd99b Giorgos Verigakis
            permissions['read'].append('*')
485 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
486 1e28ba40 Christos Stavrakakis
487 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
488 7bd1d3b5 Giorgos Verigakis
    def update(self, image_id, params):
489 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
490 15137c54 Giorgos Verigakis
        assert image, "Image not found"
491 1e28ba40 Christos Stavrakakis
492 7bd1d3b5 Giorgos Verigakis
        location = image['location']
493 7bd1d3b5 Giorgos Verigakis
        is_public = params.pop('is_public', None)
494 7e00b33e Giorgos Verigakis
        if is_public is not None:
495 7bd1d3b5 Giorgos Verigakis
            permissions = self._get_permissions(location)
496 f5afd99b Giorgos Verigakis
            read = set(permissions.get('read', []))
497 7e00b33e Giorgos Verigakis
            if is_public:
498 f5afd99b Giorgos Verigakis
                read.add('*')
499 7e00b33e Giorgos Verigakis
            else:
500 f5afd99b Giorgos Verigakis
                read.discard('*')
501 f5afd99b Giorgos Verigakis
            permissions['read'] = list(read)
502 7bd1d3b5 Giorgos Verigakis
            self.backend._update_permissions(location, permissions)
503 1e28ba40 Christos Stavrakakis
504 7bd1d3b5 Giorgos Verigakis
        meta = {}
505 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
506 7bd1d3b5 Giorgos Verigakis
        meta.update(**params)
507 1e28ba40 Christos Stavrakakis
508 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
509 bfd9f988 Giorgos Verigakis
        return self.get_image(image_id)