Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (16 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 5f01e1e6 Kostas Papadimitriou
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 6ef51e9f Giorgos Verigakis
    def iter(self):
259 6ef51e9f Giorgos Verigakis
        """Iter over all images available to the user"""
260 1e28ba40 Christos Stavrakakis
261 6ef51e9f Giorgos Verigakis
        backend = self.backend
262 6ef51e9f Giorgos Verigakis
        for account in backend.list_accounts(self.user):
263 6ef51e9f Giorgos Verigakis
            for container in backend.list_containers(self.user, account,
264 6ef51e9f Giorgos Verigakis
                                                     shared=True):
265 6ef51e9f Giorgos Verigakis
                for path, version_id in backend.list_objects(self.user,
266 6ef51e9f Giorgos Verigakis
                        account, container, domain=PLANKTON_DOMAIN):
267 6ef51e9f Giorgos Verigakis
                    location = get_location(account, container, path)
268 6ef51e9f Giorgos Verigakis
                    image = self._get_image(location)
269 6ef51e9f Giorgos Verigakis
                    if image:
270 6ef51e9f Giorgos Verigakis
                        yield image
271 1e28ba40 Christos Stavrakakis
272 d19e8f77 Giorgos Verigakis
    def iter_public(self, filters=None):
273 d19e8f77 Giorgos Verigakis
        filters = filters or {}
274 0a72907b Giorgos Verigakis
        backend = self.backend
275 1e28ba40 Christos Stavrakakis
276 bfd9f988 Giorgos Verigakis
        keys = [PLANKTON_PREFIX + 'name']
277 434d1e68 Giorgos Verigakis
        size_range = (None, None)
278 1e28ba40 Christos Stavrakakis
279 f5afd99b Giorgos Verigakis
        for key, val in filters.items():
280 0a72907b Giorgos Verigakis
            if key == 'size_min':
281 434d1e68 Giorgos Verigakis
                size_range = (int(val), size_range[1])
282 0a72907b Giorgos Verigakis
            elif key == 'size_max':
283 434d1e68 Giorgos Verigakis
                size_range = (size_range[0], int(val))
284 f5afd99b Giorgos Verigakis
            else:
285 434d1e68 Giorgos Verigakis
                keys.append('%s = %s' % (PLANKTON_PREFIX + key, val))
286 1e28ba40 Christos Stavrakakis
287 0a72907b Giorgos Verigakis
        for account in backend.list_accounts(None):
288 0a72907b Giorgos Verigakis
            for container in backend.list_containers(None, account,
289 0a72907b Giorgos Verigakis
                                                     shared=True):
290 0a72907b Giorgos Verigakis
                for path, version_id in backend.list_objects(None, account,
291 b882c201 Giorgos Verigakis
                        container, domain=PLANKTON_DOMAIN, keys=keys,
292 b882c201 Giorgos Verigakis
                        shared=True, size_range=size_range):
293 0a72907b Giorgos Verigakis
                    location = get_location(account, container, path)
294 0a72907b Giorgos Verigakis
                    image = self._get_image(location)
295 0a72907b Giorgos Verigakis
                    if image:
296 0a72907b Giorgos Verigakis
                        yield image
297 1e28ba40 Christos Stavrakakis
298 8482ef55 Giorgos Verigakis
    def iter_shared(self, member):
299 8482ef55 Giorgos Verigakis
        """Iterate over image ids shared to this member"""
300 1e28ba40 Christos Stavrakakis
301 0a72907b Giorgos Verigakis
        backend = self.backend
302 1e28ba40 Christos Stavrakakis
303 0a72907b Giorgos Verigakis
        # To get the list we connect as member and get the list shared by us
304 0a72907b Giorgos Verigakis
        for container in  backend.list_containers(member, self.user):
305 bfd9f988 Giorgos Verigakis
            for object, version_id in backend.list_objects(member, self.user,
306 b882c201 Giorgos Verigakis
                    container, domain=PLANKTON_DOMAIN):
307 0a72907b Giorgos Verigakis
                try:
308 bfd9f988 Giorgos Verigakis
                    location = get_location(self.user, container, object)
309 bfd9f988 Giorgos Verigakis
                    meta = backend.get_object_meta(member, self.user,
310 bfd9f988 Giorgos Verigakis
                            container, object, PLANKTON_DOMAIN)
311 0a72907b Giorgos Verigakis
                    if PLANKTON_PREFIX + 'name' in meta:
312 0a72907b Giorgos Verigakis
                        yield meta['uuid']
313 89b2e9b8 Giorgos Verigakis
                except (NameError, NotAllowedError):
314 0a72907b Giorgos Verigakis
                    continue
315 1e28ba40 Christos Stavrakakis
316 6ef51e9f Giorgos Verigakis
    def list(self):
317 6ef51e9f Giorgos Verigakis
        """Iter over all images available to the user"""
318 1e28ba40 Christos Stavrakakis
319 6ef51e9f Giorgos Verigakis
        return list(self.iter())
320 1e28ba40 Christos Stavrakakis
321 7bd1d3b5 Giorgos Verigakis
    def list_public(self, filters, params):
322 f5afd99b Giorgos Verigakis
        images = list(self.iter_public(filters))
323 7bd1d3b5 Giorgos Verigakis
        key = itemgetter(params.get('sort_key', 'created_at'))
324 7bd1d3b5 Giorgos Verigakis
        reverse = params.get('sort_dir', 'desc') == 'desc'
325 f5afd99b Giorgos Verigakis
        images.sort(key=key, reverse=reverse)
326 f5afd99b Giorgos Verigakis
        return images
327 1e28ba40 Christos Stavrakakis
328 f5afd99b Giorgos Verigakis
    def list_users(self, image_id):
329 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
330 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
331 1e28ba40 Christos Stavrakakis
332 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(image['location'])
333 f5afd99b Giorgos Verigakis
        return [user for user in permissions.get('read', []) if user != '*']
334 1e28ba40 Christos Stavrakakis
335 c23d211a Giorgos Verigakis
    def put(self, name, f, params):
336 7bd1d3b5 Giorgos Verigakis
        assert 'checksum' not in params, "Passing a checksum is not supported"
337 7bd1d3b5 Giorgos Verigakis
        assert 'id' not in params, "Passing an ID is not supported"
338 7bd1d3b5 Giorgos Verigakis
        assert params.pop('store', 'pithos') == 'pithos', "Invalid store"
339 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('disk_format',
340 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_DISK_FORMAT) in \
341 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_DISK_FORMATS, "Invalid disk_format"
342 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('container_format',
343 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_CONTAINER_FORMAT) in \
344 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
345 1e28ba40 Christos Stavrakakis
346 bfd9f988 Giorgos Verigakis
        container = settings.DEFAULT_PLANKTON_CONTAINER
347 c23d211a Giorgos Verigakis
        filename = params.pop('filename', name)
348 bfd9f988 Giorgos Verigakis
        location = 'pithos://%s/%s/%s' % (self.user, container, filename)
349 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
350 921355f8 Giorgos Verigakis
        permissions = {'read': ['*']} if is_public else {}
351 c23d211a Giorgos Verigakis
        size = params.pop('size', None)
352 1e28ba40 Christos Stavrakakis
353 c23d211a Giorgos Verigakis
        hashmap, size = self._store(f, size)
354 1e28ba40 Christos Stavrakakis
355 7bd1d3b5 Giorgos Verigakis
        meta = {}
356 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
357 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
358 1e28ba40 Christos Stavrakakis
359 7bd1d3b5 Giorgos Verigakis
        self._update(location, size, hashmap, meta, permissions)
360 0a72907b Giorgos Verigakis
        return self._get_image(location)
361 1e28ba40 Christos Stavrakakis
362 f5afd99b Giorgos Verigakis
    def register(self, name, location, params):
363 7bd1d3b5 Giorgos Verigakis
        assert 'id' not in params, "Passing an ID is not supported"
364 7bd1d3b5 Giorgos Verigakis
        assert location.startswith('pithos://'), "Invalid location"
365 7bd1d3b5 Giorgos Verigakis
        assert params.pop('store', 'pithos') == 'pithos', "Invalid store"
366 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('disk_format',
367 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_DISK_FORMAT) in \
368 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_DISK_FORMATS, "Invalid disk_format"
369 7bd1d3b5 Giorgos Verigakis
        assert params.setdefault('container_format',
370 7bd1d3b5 Giorgos Verigakis
                settings.DEFAULT_CONTAINER_FORMAT) in \
371 7bd1d3b5 Giorgos Verigakis
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
372 1e28ba40 Christos Stavrakakis
373 1e28ba40 Christos Stavrakakis
        # user = self.user
374 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
375 1e28ba40 Christos Stavrakakis
376 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
377 06c8fb11 Giorgos Verigakis
        assert meta, "File not found"
378 1e28ba40 Christos Stavrakakis
379 bfd9f988 Giorgos Verigakis
        size = int(params.pop('size', meta['bytes']))
380 0a72907b Giorgos Verigakis
        if size != meta['bytes']:
381 c34de90f Giorgos Verigakis
            raise BackendException("Invalid size")
382 1e28ba40 Christos Stavrakakis
383 0a72907b Giorgos Verigakis
        checksum = params.pop('checksum', meta['hash'])
384 0a72907b Giorgos Verigakis
        if checksum != meta['hash']:
385 c34de90f Giorgos Verigakis
            raise BackendException("Invalid checksum")
386 1e28ba40 Christos Stavrakakis
387 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
388 921355f8 Giorgos Verigakis
        permissions = {'read': ['*']} if is_public else {}
389 1e28ba40 Christos Stavrakakis
390 f5afd99b Giorgos Verigakis
        meta = {}
391 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
392 0a72907b Giorgos Verigakis
        meta.update(name=name, status='available', **params)
393 1e28ba40 Christos Stavrakakis
394 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
395 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
396 0a72907b Giorgos Verigakis
        return self._get_image(location)
397 1e28ba40 Christos Stavrakakis
398 f5afd99b Giorgos Verigakis
    def remove_user(self, image_id, user):
399 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
400 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
401 1e28ba40 Christos Stavrakakis
402 7bd1d3b5 Giorgos Verigakis
        location = image['location']
403 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
404 f5afd99b Giorgos Verigakis
        try:
405 f5afd99b Giorgos Verigakis
            permissions.get('read', []).remove(user)
406 f5afd99b Giorgos Verigakis
        except ValueError:
407 f5afd99b Giorgos Verigakis
            return      # User did not have access anyway
408 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
409 1e28ba40 Christos Stavrakakis
410 f5afd99b Giorgos Verigakis
    def replace_users(self, image_id, users):
411 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
412 f5afd99b Giorgos Verigakis
        assert image, "Image not found"
413 1e28ba40 Christos Stavrakakis
414 7bd1d3b5 Giorgos Verigakis
        location = image['location']
415 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
416 f5afd99b Giorgos Verigakis
        permissions['read'] = users
417 f5afd99b Giorgos Verigakis
        if image.get('is_public', False):
418 f5afd99b Giorgos Verigakis
            permissions['read'].append('*')
419 7bd1d3b5 Giorgos Verigakis
        self._update_permissions(location, permissions)
420 1e28ba40 Christos Stavrakakis
421 7bd1d3b5 Giorgos Verigakis
    def update(self, image_id, params):
422 bfd9f988 Giorgos Verigakis
        image = self.get_image(image_id)
423 15137c54 Giorgos Verigakis
        assert image, "Image not found"
424 1e28ba40 Christos Stavrakakis
425 7bd1d3b5 Giorgos Verigakis
        location = image['location']
426 7bd1d3b5 Giorgos Verigakis
        is_public = params.pop('is_public', None)
427 7e00b33e Giorgos Verigakis
        if is_public is not None:
428 7bd1d3b5 Giorgos Verigakis
            permissions = self._get_permissions(location)
429 f5afd99b Giorgos Verigakis
            read = set(permissions.get('read', []))
430 7e00b33e Giorgos Verigakis
            if is_public:
431 f5afd99b Giorgos Verigakis
                read.add('*')
432 7e00b33e Giorgos Verigakis
            else:
433 f5afd99b Giorgos Verigakis
                read.discard('*')
434 f5afd99b Giorgos Verigakis
            permissions['read'] = list(read)
435 7bd1d3b5 Giorgos Verigakis
            self.backend._update_permissions(location, permissions)
436 1e28ba40 Christos Stavrakakis
437 7bd1d3b5 Giorgos Verigakis
        meta = {}
438 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
439 7bd1d3b5 Giorgos Verigakis
        meta.update(**params)
440 1e28ba40 Christos Stavrakakis
441 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
442 bfd9f988 Giorgos Verigakis
        return self.get_image(image_id)