Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (17.1 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 2db7d9df Christos Stavrakakis
from functools import wraps
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 c34de90f Giorgos Verigakis
64 c34de90f Giorgos Verigakis
65 0a72907b Giorgos Verigakis
PLANKTON_DOMAIN = 'plankton'
66 bfd9f988 Giorgos Verigakis
PLANKTON_PREFIX = 'plankton:'
67 7bd1d3b5 Giorgos Verigakis
PROPERTY_PREFIX = 'property:'
68 7bd1d3b5 Giorgos Verigakis
69 bfd9f988 Giorgos Verigakis
PLANKTON_META = ('container_format', 'disk_format', 'name', 'properties',
70 bfd9f988 Giorgos Verigakis
                 'status')
71 c34de90f Giorgos Verigakis
72 c34de90f Giorgos Verigakis
73 7bd1d3b5 Giorgos Verigakis
def get_location(account, container, object):
74 7bd1d3b5 Giorgos Verigakis
    assert '/' not in account, "Invalid account"
75 7bd1d3b5 Giorgos Verigakis
    assert '/' not in container, "Invalid container"
76 7bd1d3b5 Giorgos Verigakis
    return 'pithos://%s/%s/%s' % (account, container, object)
77 7bd1d3b5 Giorgos Verigakis
78 7784ab88 Christos Stavrakakis
79 7bd1d3b5 Giorgos Verigakis
def split_location(location):
80 7bd1d3b5 Giorgos Verigakis
    """Returns (accout, container, object) from a location string"""
81 7bd1d3b5 Giorgos Verigakis
    t = location.split('/', 4)
82 7bd1d3b5 Giorgos Verigakis
    assert len(t) == 5, "Invalid location"
83 7bd1d3b5 Giorgos Verigakis
    return t[2:5]
84 7bd1d3b5 Giorgos Verigakis
85 7bd1d3b5 Giorgos Verigakis
86 0a72907b Giorgos Verigakis
class BackendException(Exception):
87 0a72907b Giorgos Verigakis
    pass
88 c34de90f Giorgos Verigakis
89 c34de90f Giorgos Verigakis
90 2db7d9df Christos Stavrakakis
class NotAllowedError(BackendException):
91 2db7d9df Christos Stavrakakis
    pass
92 2db7d9df Christos Stavrakakis
93 2db7d9df Christos Stavrakakis
94 7784ab88 Christos Stavrakakis
from pithos.backends.util import PithosBackendPool
95 7784ab88 Christos Stavrakakis
POOL_SIZE = 8
96 7784ab88 Christos Stavrakakis
_pithos_backend_pool = \
97 cc92b70f Christos Stavrakakis
    PithosBackendPool(POOL_SIZE,
98 cc92b70f Christos Stavrakakis
                      db_connection=settings.BACKEND_DB_CONNECTION,
99 cc92b70f Christos Stavrakakis
                      block_path=settings.BACKEND_BLOCK_PATH)
100 7784ab88 Christos Stavrakakis
101 7784ab88 Christos Stavrakakis
102 7784ab88 Christos Stavrakakis
def get_pithos_backend():
103 7784ab88 Christos Stavrakakis
    return _pithos_backend_pool.pool_get()
104 7784ab88 Christos Stavrakakis
105 7784ab88 Christos Stavrakakis
106 2db7d9df Christos Stavrakakis
def handle_backend_exceptions(func):
107 2db7d9df Christos Stavrakakis
    @wraps(func)
108 2db7d9df Christos Stavrakakis
    def wrapper(*args, **kwargs):
109 2db7d9df Christos Stavrakakis
        try:
110 2db7d9df Christos Stavrakakis
            return func(*args, **kwargs)
111 2db7d9df Christos Stavrakakis
        except PithosNotAllowedError:
112 2db7d9df Christos Stavrakakis
            raise NotAllowedError()
113 2db7d9df Christos Stavrakakis
    return wrapper
114 2db7d9df Christos Stavrakakis
115 2db7d9df Christos Stavrakakis
116 ffab341c Christos Stavrakakis
class ImageBackend(object):
117 f5afd99b Giorgos Verigakis
    """A wrapper arround the pithos backend to simplify image handling."""
118 1e28ba40 Christos Stavrakakis
119 c34de90f Giorgos Verigakis
    def __init__(self, user):
120 c34de90f Giorgos Verigakis
        self.user = user
121 7784ab88 Christos Stavrakakis
122 c23d211a Giorgos Verigakis
        original_filters = warnings.filters
123 c23d211a Giorgos Verigakis
        warnings.simplefilter('ignore')         # Suppress SQLAlchemy warnings
124 7784ab88 Christos Stavrakakis
        self.backend = get_pithos_backend()
125 c23d211a Giorgos Verigakis
        warnings.filters = original_filters     # Restore warnings
126 7784ab88 Christos Stavrakakis
127 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
128 7bd1d3b5 Giorgos Verigakis
    def _get_image(self, location):
129 f5afd99b Giorgos Verigakis
        def format_timestamp(t):
130 f5afd99b Giorgos Verigakis
            return strftime('%Y-%m-%d %H:%M:%S', gmtime(t))
131 1e28ba40 Christos Stavrakakis
132 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
133 1e28ba40 Christos Stavrakakis
134 7bd1d3b5 Giorgos Verigakis
        try:
135 7bd1d3b5 Giorgos Verigakis
            versions = self.backend.list_versions(self.user, account,
136 cc92b70f Christos Stavrakakis
                                                  container, object)
137 7bd1d3b5 Giorgos Verigakis
        except NameError:
138 7bd1d3b5 Giorgos Verigakis
            return None
139 1e28ba40 Christos Stavrakakis
140 7bd1d3b5 Giorgos Verigakis
        image = {}
141 1e28ba40 Christos Stavrakakis
142 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
143 7bd1d3b5 Giorgos Verigakis
        if meta:
144 7bd1d3b5 Giorgos Verigakis
            image['deleted_at'] = ''
145 7bd1d3b5 Giorgos Verigakis
        else:
146 7bd1d3b5 Giorgos Verigakis
            # Object was deleted, use the latest version
147 7bd1d3b5 Giorgos Verigakis
            version, timestamp = versions[-1]
148 7bd1d3b5 Giorgos Verigakis
            meta = self._get_meta(location, version)
149 7bd1d3b5 Giorgos Verigakis
            image['deleted_at'] = format_timestamp(timestamp)
150 1e28ba40 Christos Stavrakakis
151 0a72907b Giorgos Verigakis
        if PLANKTON_PREFIX + 'name' not in meta:
152 0a72907b Giorgos Verigakis
            return None     # Not a Plankton image
153 1e28ba40 Christos Stavrakakis
154 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
155 1e28ba40 Christos Stavrakakis
156 0a72907b Giorgos Verigakis
        image['checksum'] = meta['hash']
157 7bd1d3b5 Giorgos Verigakis
        image['created_at'] = format_timestamp(versions[0][1])
158 0a72907b Giorgos Verigakis
        image['id'] = meta['uuid']
159 f5afd99b Giorgos Verigakis
        image['is_public'] = '*' in permissions.get('read', [])
160 7bd1d3b5 Giorgos Verigakis
        image['location'] = location
161 7bd1d3b5 Giorgos Verigakis
        image['owner'] = account
162 0a72907b Giorgos Verigakis
        image['size'] = meta['bytes']
163 7bd1d3b5 Giorgos Verigakis
        image['store'] = 'pithos'
164 0a72907b Giorgos Verigakis
        image['updated_at'] = format_timestamp(meta['modified'])
165 7bd1d3b5 Giorgos Verigakis
        image['properties'] = {}
166 1e28ba40 Christos Stavrakakis
167 7bd1d3b5 Giorgos Verigakis
        for key, val in meta.items():
168 bfd9f988 Giorgos Verigakis
            if not key.startswith(PLANKTON_PREFIX):
169 bfd9f988 Giorgos Verigakis
                continue
170 bfd9f988 Giorgos Verigakis
            key = key[len(PLANKTON_PREFIX):]
171 bfd9f988 Giorgos Verigakis
            if key == 'properties':
172 bfd9f988 Giorgos Verigakis
                val = json.loads(val)
173 bfd9f988 Giorgos Verigakis
            if key in PLANKTON_META:
174 7bd1d3b5 Giorgos Verigakis
                image[key] = val
175 1e28ba40 Christos Stavrakakis
176 0a72907b Giorgos Verigakis
        return image
177 1e28ba40 Christos Stavrakakis
178 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
179 bfd9f988 Giorgos Verigakis
    def _get_meta(self, location, version=None):
180 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
181 7bd1d3b5 Giorgos Verigakis
        try:
182 bfd9f988 Giorgos Verigakis
            return self.backend.get_object_meta(self.user, account, container,
183 cc92b70f Christos Stavrakakis
                                                object, PLANKTON_DOMAIN,
184 cc92b70f Christos Stavrakakis
                                                version)
185 7bd1d3b5 Giorgos Verigakis
        except NameError:
186 7bd1d3b5 Giorgos Verigakis
            return None
187 1e28ba40 Christos Stavrakakis
188 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
189 7bd1d3b5 Giorgos Verigakis
    def _get_permissions(self, location):
190 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
191 cc92b70f Christos Stavrakakis
        _a, _p, permissions = self.backend.get_object_permissions(self.user,
192 cc92b70f Christos Stavrakakis
                                                                  account,
193 cc92b70f Christos Stavrakakis
                                                                  container,
194 cc92b70f Christos Stavrakakis
                                                                  object)
195 f5afd99b Giorgos Verigakis
        return permissions
196 1e28ba40 Christos Stavrakakis
197 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
198 c23d211a Giorgos Verigakis
    def _store(self, f, size=None):
199 7bd1d3b5 Giorgos Verigakis
        """Breaks data into blocks and stores them in the backend"""
200 1e28ba40 Christos Stavrakakis
201 c23d211a Giorgos Verigakis
        bytes = 0
202 f5afd99b Giorgos Verigakis
        hashmap = []
203 7bd1d3b5 Giorgos Verigakis
        backend = self.backend
204 c23d211a Giorgos Verigakis
        blocksize = backend.block_size
205 1e28ba40 Christos Stavrakakis
206 c23d211a Giorgos Verigakis
        data = f.read(blocksize)
207 7bd1d3b5 Giorgos Verigakis
        while data:
208 c23d211a Giorgos Verigakis
            hash = backend.put_block(data)
209 f5afd99b Giorgos Verigakis
            hashmap.append(hash)
210 c23d211a Giorgos Verigakis
            bytes += len(data)
211 c23d211a Giorgos Verigakis
            data = f.read(blocksize)
212 1e28ba40 Christos Stavrakakis
213 c23d211a Giorgos Verigakis
        if size and size != bytes:
214 c23d211a Giorgos Verigakis
            raise BackendException("Invalid size")
215 1e28ba40 Christos Stavrakakis
216 c23d211a Giorgos Verigakis
        return hashmap, bytes
217 1e28ba40 Christos Stavrakakis
218 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
219 7bd1d3b5 Giorgos Verigakis
    def _update(self, location, size, hashmap, meta, permissions):
220 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
221 7bd1d3b5 Giorgos Verigakis
        self.backend.update_object_hashmap(self.user, account, container,
222 cc92b70f Christos Stavrakakis
                                           object, size, hashmap, '',
223 cc92b70f Christos Stavrakakis
                                           PLANKTON_DOMAIN,
224 cc92b70f Christos Stavrakakis
                                           permissions=permissions)
225 0a72907b Giorgos Verigakis
        self._update_meta(location, meta, replace=True)
226 1e28ba40 Christos Stavrakakis
227 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
228 0a72907b Giorgos Verigakis
    def _update_meta(self, location, meta, replace=False):
229 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
230 1e28ba40 Christos Stavrakakis
231 bfd9f988 Giorgos Verigakis
        prefixed = {}
232 0a72907b Giorgos Verigakis
        for key, val in meta.items():
233 bfd9f988 Giorgos Verigakis
            if key == 'properties':
234 bfd9f988 Giorgos Verigakis
                val = json.dumps(val)
235 bfd9f988 Giorgos Verigakis
            if key in PLANKTON_META:
236 bfd9f988 Giorgos Verigakis
                prefixed[PLANKTON_PREFIX + key] = val
237 1e28ba40 Christos Stavrakakis
238 bfd9f988 Giorgos Verigakis
        self.backend.update_object_meta(self.user, account, container, object,
239 cc92b70f Christos Stavrakakis
                                        PLANKTON_DOMAIN, prefixed, replace)
240 1e28ba40 Christos Stavrakakis
241 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
242 7bd1d3b5 Giorgos Verigakis
    def _update_permissions(self, location, permissions):
243 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
244 7bd1d3b5 Giorgos Verigakis
        self.backend.update_object_permissions(self.user, account, container,
245 cc92b70f Christos Stavrakakis
                                               object, permissions)
246 1e28ba40 Christos Stavrakakis
247 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
248 f5afd99b Giorgos Verigakis
    def add_user(self, image_id, user):
249 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
250 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
251 1e28ba40 Christos Stavrakakis
252 7bd1d3b5 Giorgos Verigakis
        location = image['location']
253 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
254 f5afd99b Giorgos Verigakis
        read = set(permissions.get('read', []))
255 f5afd99b Giorgos Verigakis
        read.add(user)
256 f5afd99b Giorgos Verigakis
        permissions['read'] = list(read)
257 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
258 1e28ba40 Christos Stavrakakis
259 f5afd99b Giorgos Verigakis
    def close(self):
260 f5afd99b Giorgos Verigakis
        self.backend.close()
261 1e28ba40 Christos Stavrakakis
262 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
263 6ef51e9f Giorgos Verigakis
    def delete(self, image_id):
264 6ef51e9f Giorgos Verigakis
        image = self.get_image(image_id)
265 6ef51e9f Giorgos Verigakis
        account, container, object = split_location(image['location'])
266 6ef51e9f Giorgos Verigakis
        self.backend.delete_object(self.user, account, container, object)
267 1e28ba40 Christos Stavrakakis
268 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
269 7bd1d3b5 Giorgos Verigakis
    def get_data(self, location):
270 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
271 7bd1d3b5 Giorgos Verigakis
        size, hashmap = self.backend.get_object_hashmap(self.user, account,
272 cc92b70f Christos Stavrakakis
                                                        container, object)
273 f5afd99b Giorgos Verigakis
        data = ''.join(self.backend.get_block(hash) for hash in hashmap)
274 f5afd99b Giorgos Verigakis
        assert len(data) == size
275 f5afd99b Giorgos Verigakis
        return data
276 1e28ba40 Christos Stavrakakis
277 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
278 bfd9f988 Giorgos Verigakis
    def get_image(self, image_id):
279 0a72907b Giorgos Verigakis
        try:
280 0a72907b Giorgos Verigakis
            account, container, object = self.backend.get_uuid(self.user,
281 cc92b70f Christos Stavrakakis
                                                               image_id)
282 0a72907b Giorgos Verigakis
        except NameError:
283 0a72907b Giorgos Verigakis
            return None
284 1e28ba40 Christos Stavrakakis
285 0a72907b Giorgos Verigakis
        location = get_location(account, container, object)
286 0a72907b Giorgos Verigakis
        return self._get_image(location)
287 1e28ba40 Christos Stavrakakis
288 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
289 aed77afe Christos Stavrakakis
    def _iter(self, public=False, filters=None, shared_from=None):
290 d19e8f77 Giorgos Verigakis
        filters = filters or {}
291 1e28ba40 Christos Stavrakakis
292 aed77afe Christos Stavrakakis
        # Fix keys
293 bfd9f988 Giorgos Verigakis
        keys = [PLANKTON_PREFIX + 'name']
294 434d1e68 Giorgos Verigakis
        size_range = (None, None)
295 f5afd99b Giorgos Verigakis
        for key, val in filters.items():
296 0a72907b Giorgos Verigakis
            if key == 'size_min':
297 c3bcaeff Stratos Psomadakis
                size_range = (val, size_range[1])
298 0a72907b Giorgos Verigakis
            elif key == 'size_max':
299 c3bcaeff Stratos Psomadakis
                size_range = (size_range[0], val)
300 f5afd99b Giorgos Verigakis
            else:
301 434d1e68 Giorgos Verigakis
                keys.append('%s = %s' % (PLANKTON_PREFIX + key, val))
302 1e28ba40 Christos Stavrakakis
303 aed77afe Christos Stavrakakis
        backend = self.backend
304 aed77afe Christos Stavrakakis
        if shared_from:
305 aed77afe Christos Stavrakakis
            # To get shared images, we connect as shared_from member and
306 aed77afe Christos Stavrakakis
            # get the list shared by us
307 aed77afe Christos Stavrakakis
            user = shared_from
308 aed77afe Christos Stavrakakis
            accounts = [self.user]
309 aed77afe Christos Stavrakakis
        else:
310 aed77afe Christos Stavrakakis
            user = None if public else self.user
311 aed77afe Christos Stavrakakis
            accounts = backend.list_accounts(user)
312 aed77afe Christos Stavrakakis
313 aed77afe Christos Stavrakakis
        for account in accounts:
314 aed77afe Christos Stavrakakis
            for container in backend.list_containers(user, account,
315 0a72907b Giorgos Verigakis
                                                     shared=True):
316 aed77afe Christos Stavrakakis
                for path, _ in backend.list_objects(user, account, container,
317 aed77afe Christos Stavrakakis
                                                    domain=PLANKTON_DOMAIN,
318 aed77afe Christos Stavrakakis
                                                    keys=keys, shared=True,
319 aed77afe Christos Stavrakakis
                                                    size_range=size_range):
320 0a72907b Giorgos Verigakis
                    location = get_location(account, container, path)
321 0a72907b Giorgos Verigakis
                    image = self._get_image(location)
322 0a72907b Giorgos Verigakis
                    if image:
323 0a72907b Giorgos Verigakis
                        yield image
324 1e28ba40 Christos Stavrakakis
325 aed77afe Christos Stavrakakis
    def iter(self, filters=None):
326 aed77afe Christos Stavrakakis
        """Iter over all images available to the user"""
327 aed77afe Christos Stavrakakis
        return self._iter(filters=filters)
328 1e28ba40 Christos Stavrakakis
329 aed77afe Christos Stavrakakis
    def iter_public(self, filters=None):
330 aed77afe Christos Stavrakakis
        """Iter over public images"""
331 aed77afe Christos Stavrakakis
        return self._iter(public=True, filters=filters)
332 1e28ba40 Christos Stavrakakis
333 aed77afe Christos Stavrakakis
    def iter_shared(self, filters=None, member=None):
334 aed77afe Christos Stavrakakis
        """Iter over images shared to member"""
335 d0e8984d Christos Stavrakakis
        return self._iter(filters=filters, shared_from=member)
336 1e28ba40 Christos Stavrakakis
337 aed77afe Christos Stavrakakis
    def list(self, filters=None, params={}):
338 aed77afe Christos Stavrakakis
        """Return all images available to the user"""
339 aed77afe Christos Stavrakakis
        images = list(self.iter(filters))
340 aed77afe Christos Stavrakakis
        key = itemgetter(params.get('sort_key', 'created_at'))
341 aed77afe Christos Stavrakakis
        reverse = params.get('sort_dir', 'desc') == 'desc'
342 aed77afe Christos Stavrakakis
        images.sort(key=key, reverse=reverse)
343 aed77afe Christos Stavrakakis
        return images
344 1e28ba40 Christos Stavrakakis
345 aed77afe Christos Stavrakakis
    def list_public(self, filters, params={}):
346 aed77afe Christos Stavrakakis
        """Return public images"""
347 f5afd99b Giorgos Verigakis
        images = list(self.iter_public(filters))
348 7bd1d3b5 Giorgos Verigakis
        key = itemgetter(params.get('sort_key', 'created_at'))
349 7bd1d3b5 Giorgos Verigakis
        reverse = params.get('sort_dir', 'desc') == 'desc'
350 f5afd99b Giorgos Verigakis
        images.sort(key=key, reverse=reverse)
351 f5afd99b Giorgos Verigakis
        return images
352 1e28ba40 Christos Stavrakakis
353 f5afd99b Giorgos Verigakis
    def list_users(self, image_id):
354 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
355 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
356 1e28ba40 Christos Stavrakakis
357 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(image['location'])
358 f5afd99b Giorgos Verigakis
        return [user for user in permissions.get('read', []) if user != '*']
359 1e28ba40 Christos Stavrakakis
360 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
361 c23d211a Giorgos Verigakis
    def put(self, name, f, params):
362 7bd1d3b5 Giorgos Verigakis
        assert 'checksum' not in params, "Passing a checksum is not supported"
363 7bd1d3b5 Giorgos Verigakis
        assert 'id' not in params, "Passing an ID is not supported"
364 7bd1d3b5 Giorgos Verigakis
        assert params.pop('store', 'pithos') == 'pithos', "Invalid store"
365 cc92b70f Christos Stavrakakis
        disk_format = params.setdefault('disk_format',
366 cc92b70f Christos Stavrakakis
                                        settings.DEFAULT_DISK_FORMAT)
367 cc92b70f Christos Stavrakakis
        assert disk_format in settings.ALLOWED_DISK_FORMATS,\
368 cc92b70f Christos Stavrakakis
            "Invalid disk_format"
369 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('container_format',
370 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_CONTAINER_FORMAT) in \
371 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
372 1e28ba40 Christos Stavrakakis
373 bfd9f988 Giorgos Verigakis
        container = settings.DEFAULT_PLANKTON_CONTAINER
374 c23d211a Giorgos Verigakis
        filename = params.pop('filename', name)
375 bfd9f988 Giorgos Verigakis
        location = 'pithos://%s/%s/%s' % (self.user, container, filename)
376 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
377 921355f8 Giorgos Verigakis
        permissions = {'read': ['*']} if is_public else {}
378 c23d211a Giorgos Verigakis
        size = params.pop('size', None)
379 1e28ba40 Christos Stavrakakis
380 c23d211a Giorgos Verigakis
        hashmap, size = self._store(f, size)
381 1e28ba40 Christos Stavrakakis
382 7bd1d3b5 Giorgos Verigakis
        meta = {}
383 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
384 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
385 1e28ba40 Christos Stavrakakis
386 7bd1d3b5 Giorgos Verigakis
        self._update(location, size, hashmap, meta, permissions)
387 0a72907b Giorgos Verigakis
        return self._get_image(location)
388 1e28ba40 Christos Stavrakakis
389 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
390 f5afd99b Giorgos Verigakis
    def register(self, name, location, params):
391 7bd1d3b5 Giorgos Verigakis
        assert 'id' not in params, "Passing an ID is not supported"
392 7bd1d3b5 Giorgos Verigakis
        assert location.startswith('pithos://'), "Invalid location"
393 7bd1d3b5 Giorgos Verigakis
        assert params.pop('store', 'pithos') == 'pithos', "Invalid store"
394 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('disk_format',
395 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_DISK_FORMAT) in \
396 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_DISK_FORMATS, "Invalid disk_format"
397 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('container_format',
398 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_CONTAINER_FORMAT) in \
399 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
400 1e28ba40 Christos Stavrakakis
401 1e28ba40 Christos Stavrakakis
        # user = self.user
402 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
403 1e28ba40 Christos Stavrakakis
404 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
405 06c8fb11 Giorgos Verigakis
        assert meta, "File not found"
406 1e28ba40 Christos Stavrakakis
407 bfd9f988 Giorgos Verigakis
        size = int(params.pop('size', meta['bytes']))
408 0a72907b Giorgos Verigakis
        if size != meta['bytes']:
409 c34de90f Giorgos Verigakis
            raise BackendException("Invalid size")
410 1e28ba40 Christos Stavrakakis
411 0a72907b Giorgos Verigakis
        checksum = params.pop('checksum', meta['hash'])
412 0a72907b Giorgos Verigakis
        if checksum != meta['hash']:
413 c34de90f Giorgos Verigakis
            raise BackendException("Invalid checksum")
414 1e28ba40 Christos Stavrakakis
415 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
416 aed77afe Christos Stavrakakis
        if is_public:
417 aed77afe Christos Stavrakakis
            permissions = {'read': ['*']}
418 aed77afe Christos Stavrakakis
        else:
419 aed77afe Christos Stavrakakis
            permissions = {'read': [self.user]}
420 1e28ba40 Christos Stavrakakis
421 f5afd99b Giorgos Verigakis
        meta = {}
422 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
423 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
424 1e28ba40 Christos Stavrakakis
425 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
426 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
427 0a72907b Giorgos Verigakis
        return self._get_image(location)
428 1e28ba40 Christos Stavrakakis
429 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
430 f5afd99b Giorgos Verigakis
    def remove_user(self, image_id, user):
431 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
432 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
433 1e28ba40 Christos Stavrakakis
434 7bd1d3b5 Giorgos Verigakis
        location = image['location']
435 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
436 f5afd99b Giorgos Verigakis
        try:
437 f5afd99b Giorgos Verigakis
            permissions.get('read', []).remove(user)
438 f5afd99b Giorgos Verigakis
        except ValueError:
439 f5afd99b Giorgos Verigakis
            return      # User did not have access anyway
440 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
441 1e28ba40 Christos Stavrakakis
442 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
443 f5afd99b Giorgos Verigakis
    def replace_users(self, image_id, users):
444 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
445 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
446 1e28ba40 Christos Stavrakakis
447 7bd1d3b5 Giorgos Verigakis
        location = image['location']
448 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
449 f5afd99b Giorgos Verigakis
        permissions['read'] = users
450 f5afd99b Giorgos Verigakis
        if image.get('is_public', False):
451 f5afd99b Giorgos Verigakis
            permissions['read'].append('*')
452 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
453 1e28ba40 Christos Stavrakakis
454 2db7d9df Christos Stavrakakis
    @handle_backend_exceptions
455 7bd1d3b5 Giorgos Verigakis
    def update(self, image_id, params):
456 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
457 15137c54 Giorgos Verigakis
        assert image, "Image not found"
458 1e28ba40 Christos Stavrakakis
459 7bd1d3b5 Giorgos Verigakis
        location = image['location']
460 7bd1d3b5 Giorgos Verigakis
        is_public = params.pop('is_public', None)
461 7e00b33e Giorgos Verigakis
        if is_public is not None:
462 7bd1d3b5 Giorgos Verigakis
            permissions = self._get_permissions(location)
463 f5afd99b Giorgos Verigakis
            read = set(permissions.get('read', []))
464 7e00b33e Giorgos Verigakis
            if is_public:
465 f5afd99b Giorgos Verigakis
                read.add('*')
466 7e00b33e Giorgos Verigakis
            else:
467 f5afd99b Giorgos Verigakis
                read.discard('*')
468 f5afd99b Giorgos Verigakis
            permissions['read'] = list(read)
469 7bd1d3b5 Giorgos Verigakis
            self.backend._update_permissions(location, permissions)
470 1e28ba40 Christos Stavrakakis
471 7bd1d3b5 Giorgos Verigakis
        meta = {}
472 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
473 7bd1d3b5 Giorgos Verigakis
        meta.update(**params)
474 1e28ba40 Christos Stavrakakis
475 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
476 bfd9f988 Giorgos Verigakis
        return self.get_image(image_id)