Statistics
| Branch: | Tag: | Revision:

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

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