Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (16.3 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 5f01e1e6 Kostas Papadimitriou
class ImageBackend(object):
90 f5afd99b Giorgos Verigakis
    """A wrapper arround the pithos backend to simplify image handling."""
91 5f01e1e6 Kostas Papadimitriou
    
92 c34de90f Giorgos Verigakis
    def __init__(self, user):
93 c34de90f Giorgos Verigakis
        self.user = user
94 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
107 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
108 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
115 7bd1d3b5 Giorgos Verigakis
        image = {}
116 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
126 0a72907b Giorgos Verigakis
        if PLANKTON_PREFIX + 'name' not in meta:
127 0a72907b Giorgos Verigakis
            return None     # Not a Plankton image
128 5f01e1e6 Kostas Papadimitriou
        
129 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
130 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
151 0a72907b Giorgos Verigakis
        return image
152 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
182 c23d211a Giorgos Verigakis
        if size and size != bytes:
183 c23d211a Giorgos Verigakis
            raise BackendException("Invalid size")
184 5f01e1e6 Kostas Papadimitriou
        
185 c23d211a Giorgos Verigakis
        return hashmap, bytes
186 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
194 0a72907b Giorgos Verigakis
    def _update_meta(self, location, meta, replace=False):
195 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
196 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
204 bfd9f988 Giorgos Verigakis
        self.backend.update_object_meta(self.user, account, container, object,
205 bfd9f988 Giorgos Verigakis
                PLANKTON_DOMAIN, prefixed, replace)
206 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
    
223 f5afd99b Giorgos Verigakis
    def close(self):
224 f5afd99b Giorgos Verigakis
        self.backend.close()
225 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
246 0a72907b Giorgos Verigakis
        location = get_location(account, container, object)
247 0a72907b Giorgos Verigakis
        return self._get_image(location)
248 5f01e1e6 Kostas Papadimitriou
    
249 6ef51e9f Giorgos Verigakis
    def iter(self):
250 6ef51e9f Giorgos Verigakis
        """Iter over all images available to the user"""
251 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
267 bfd9f988 Giorgos Verigakis
        keys = [PLANKTON_PREFIX + 'name']
268 434d1e68 Giorgos Verigakis
        size_range = (None, None)
269 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
    
289 8482ef55 Giorgos Verigakis
    def iter_shared(self, member):
290 8482ef55 Giorgos Verigakis
        """Iterate over image ids shared to this member"""
291 5f01e1e6 Kostas Papadimitriou
        
292 0a72907b Giorgos Verigakis
        backend = self.backend
293 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
    
307 6ef51e9f Giorgos Verigakis
    def list(self):
308 6ef51e9f Giorgos Verigakis
        """Iter over all images available to the user"""
309 5f01e1e6 Kostas Papadimitriou
        
310 6ef51e9f Giorgos Verigakis
        return list(self.iter())
311 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
344 c23d211a Giorgos Verigakis
        hashmap, size = self._store(f, size)
345 5f01e1e6 Kostas Papadimitriou
        
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 5f01e1e6 Kostas Papadimitriou
        
350 7bd1d3b5 Giorgos Verigakis
        self._update(location, size, hashmap, meta, permissions)
351 0a72907b Giorgos Verigakis
        return self._get_image(location)
352 5f01e1e6 Kostas Papadimitriou
    
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 5f01e1e6 Kostas Papadimitriou
        
364 f5afd99b Giorgos Verigakis
        user = self.user
365 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
366 5f01e1e6 Kostas Papadimitriou
        
367 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
368 06c8fb11 Giorgos Verigakis
        assert meta, "File not found"
369 5f01e1e6 Kostas Papadimitriou
        
370 bfd9f988 Giorgos Verigakis
        size = int(params.pop('size', meta['bytes']))
371 0a72907b Giorgos Verigakis
        if size != meta['bytes']:
372 c34de90f Giorgos Verigakis
            raise BackendException("Invalid size")
373 5f01e1e6 Kostas Papadimitriou
        
374 0a72907b Giorgos Verigakis
        checksum = params.pop('checksum', meta['hash'])
375 0a72907b Giorgos Verigakis
        if checksum != meta['hash']:
376 c34de90f Giorgos Verigakis
            raise BackendException("Invalid checksum")
377 5f01e1e6 Kostas Papadimitriou
        
378 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
379 921355f8 Giorgos Verigakis
        permissions = {'read': ['*']} if is_public else {}
380 5f01e1e6 Kostas Papadimitriou
        
381 f5afd99b Giorgos Verigakis
        meta = {}
382 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
383 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
384 5f01e1e6 Kostas Papadimitriou
        
385 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
386 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
387 0a72907b Giorgos Verigakis
        return self._get_image(location)
388 5f01e1e6 Kostas Papadimitriou
    
389 f5afd99b Giorgos Verigakis
    def remove_user(self, image_id, user):
390 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
391 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
392 5f01e1e6 Kostas Papadimitriou
        
393 7bd1d3b5 Giorgos Verigakis
        location = image['location']
394 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
395 f5afd99b Giorgos Verigakis
        try:
396 f5afd99b Giorgos Verigakis
            permissions.get('read', []).remove(user)
397 f5afd99b Giorgos Verigakis
        except ValueError:
398 f5afd99b Giorgos Verigakis
            return      # User did not have access anyway
399 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
400 5f01e1e6 Kostas Papadimitriou
    
401 f5afd99b Giorgos Verigakis
    def replace_users(self, image_id, users):
402 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
403 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
404 5f01e1e6 Kostas Papadimitriou
        
405 7bd1d3b5 Giorgos Verigakis
        location = image['location']
406 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
407 f5afd99b Giorgos Verigakis
        permissions['read'] = users
408 f5afd99b Giorgos Verigakis
        if image.get('is_public', False):
409 f5afd99b Giorgos Verigakis
            permissions['read'].append('*')
410 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
411 5f01e1e6 Kostas Papadimitriou
    
412 7bd1d3b5 Giorgos Verigakis
    def update(self, image_id, params):
413 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
414 15137c54 Giorgos Verigakis
        assert image, "Image not found"
415 5f01e1e6 Kostas Papadimitriou
        
416 7bd1d3b5 Giorgos Verigakis
        location = image['location']
417 7bd1d3b5 Giorgos Verigakis
        is_public = params.pop('is_public', None)
418 7e00b33e Giorgos Verigakis
        if is_public is not None:
419 7bd1d3b5 Giorgos Verigakis
            permissions = self._get_permissions(location)
420 f5afd99b Giorgos Verigakis
            read = set(permissions.get('read', []))
421 7e00b33e Giorgos Verigakis
            if is_public:
422 f5afd99b Giorgos Verigakis
                read.add('*')
423 7e00b33e Giorgos Verigakis
            else:
424 f5afd99b Giorgos Verigakis
                read.discard('*')
425 f5afd99b Giorgos Verigakis
            permissions['read'] = list(read)
426 7bd1d3b5 Giorgos Verigakis
            self.backend._update_permissions(location, permissions)
427 5f01e1e6 Kostas Papadimitriou
        
428 7bd1d3b5 Giorgos Verigakis
        meta = {}
429 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
430 7bd1d3b5 Giorgos Verigakis
        meta.update(**params)
431 5f01e1e6 Kostas Papadimitriou
        
432 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
433 bfd9f988 Giorgos Verigakis
        return self.get_image(image_id)