Statistics
| Branch: | Tag: | Revision:

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

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