Statistics
| Branch: | Tag: | Revision:

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

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