Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (23 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 236223aa Kostas Papadimitriou
class PithosImageBackend(object):
90 f5afd99b Giorgos Verigakis
    """A wrapper arround the pithos backend to simplify image handling."""
91 236223aa Kostas Papadimitriou
92 c34de90f Giorgos Verigakis
    def __init__(self, user):
93 c34de90f Giorgos Verigakis
        self.user = user
94 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
107 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
108 236223aa 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 236223aa Kostas Papadimitriou
115 7bd1d3b5 Giorgos Verigakis
        image = {}
116 236223aa 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 236223aa Kostas Papadimitriou
126 0a72907b Giorgos Verigakis
        if PLANKTON_PREFIX + 'name' not in meta:
127 0a72907b Giorgos Verigakis
            return None     # Not a Plankton image
128 236223aa Kostas Papadimitriou
129 7bd1d3b5 Giorgos Verigakis
        permissions = self._get_permissions(location)
130 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
151 0a72907b Giorgos Verigakis
        return image
152 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
182 c23d211a Giorgos Verigakis
        if size and size != bytes:
183 c23d211a Giorgos Verigakis
            raise BackendException("Invalid size")
184 236223aa Kostas Papadimitriou
185 c23d211a Giorgos Verigakis
        return hashmap, bytes
186 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
223 f5afd99b Giorgos Verigakis
    def close(self):
224 f5afd99b Giorgos Verigakis
        self.backend.close()
225 236223aa 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 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
246 0a72907b Giorgos Verigakis
        location = get_location(account, container, object)
247 0a72907b Giorgos Verigakis
        return self._get_image(location)
248 236223aa Kostas Papadimitriou
249 6ef51e9f Giorgos Verigakis
    def iter(self):
250 6ef51e9f Giorgos Verigakis
        """Iter over all images available to the user"""
251 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
267 bfd9f988 Giorgos Verigakis
        keys = [PLANKTON_PREFIX + 'name']
268 434d1e68 Giorgos Verigakis
        size_range = (None, None)
269 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
289 8482ef55 Giorgos Verigakis
    def iter_shared(self, member):
290 8482ef55 Giorgos Verigakis
        """Iterate over image ids shared to this member"""
291 236223aa Kostas Papadimitriou
292 0a72907b Giorgos Verigakis
        backend = self.backend
293 236223aa 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 236223aa Kostas Papadimitriou
307 6ef51e9f Giorgos Verigakis
    def list(self):
308 6ef51e9f Giorgos Verigakis
        """Iter over all images available to the user"""
309 236223aa Kostas Papadimitriou
310 6ef51e9f Giorgos Verigakis
        return list(self.iter())
311 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
344 c23d211a Giorgos Verigakis
        hashmap, size = self._store(f, size)
345 236223aa 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 236223aa Kostas Papadimitriou
350 7bd1d3b5 Giorgos Verigakis
        self._update(location, size, hashmap, meta, permissions)
351 0a72907b Giorgos Verigakis
        return self._get_image(location)
352 236223aa 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 236223aa Kostas Papadimitriou
364 f5afd99b Giorgos Verigakis
        user = self.user
365 7bd1d3b5 Giorgos Verigakis
        account, container, object = split_location(location)
366 236223aa Kostas Papadimitriou
367 7bd1d3b5 Giorgos Verigakis
        meta = self._get_meta(location)
368 06c8fb11 Giorgos Verigakis
        assert meta, "File not found"
369 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
378 f5afd99b Giorgos Verigakis
        is_public = params.pop('is_public', False)
379 921355f8 Giorgos Verigakis
        permissions = {'read': ['*']} if is_public else {}
380 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa 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 236223aa Kostas Papadimitriou
428 7bd1d3b5 Giorgos Verigakis
        meta = {}
429 7bd1d3b5 Giorgos Verigakis
        meta['properties'] = params.pop('properties', {})
430 7bd1d3b5 Giorgos Verigakis
        meta.update(**params)
431 236223aa Kostas Papadimitriou
432 7bd1d3b5 Giorgos Verigakis
        self._update_meta(location, meta)
433 bfd9f988 Giorgos Verigakis
        return self.get_image(image_id)
434 236223aa Kostas Papadimitriou
435 236223aa Kostas Papadimitriou
436 236223aa Kostas Papadimitriou
class DummyImageBackend():
437 236223aa Kostas Papadimitriou
438 236223aa Kostas Papadimitriou
    def __init__(self, user, images=None):
439 236223aa Kostas Papadimitriou
        self.user = user
440 236223aa Kostas Papadimitriou
        self.images = images or [{"status":"available","name":"Windows","checksum":"c4524d6f6e4f04ecf0212e50bb36f70114f459b223f932f1a3fa6a995e1334f7","created_at":"2012-03-28 14:39:43","disk_format":"diskdump","updated_at":"2012-03-28 16:56:34","properties":{"kernel":"Windows NT Kernel","osfamily":"windows","users":"Administrator","gui":"Windows, Aero Theme","sortorder":"7","size":"10537","os":"windows","root_partition":"2","description":"Windows 2008 R2, Aero Desktop Experience","OS":"windows"},"location":"pithos://images@okeanos.grnet.gr/pithos/windows-2008R2-7-x86_64.diskdump","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"34138811-299e-45db-9a53-acd5f1f7693c","size":11037310976,"metadata":{"values":{"kernel":"Windows NT Kernel","osfamily":"windows","users":"Administrator","gui":"Windows, Aero Theme","sortorder":"7","size":"10537","os":"windows","root_partition":"2","description":"Windows 2008 R2, Aero Desktop Experience","OS":"windows"}},"OS":"windows","description":"Windows 2008 R2, Aero Desktop Experience","kernel":"Windows NT Kernel","GUI":""},{"status":"available","name":"CentOS","checksum":"a7517d876f2387527f35e9d8c19cba4a37419279005354ca03bd625db1cf410e","created_at":"2012-03-28 13:37:28","disk_format":"diskdump","updated_at":"2012-03-28 16:56:33","properties":{"kernel":"2.6.32","osfamily":"linux","users":"root","gui":"No GUI","sortorder":"6","size":"601","os":"centos","root_partition":"1","description":"CentOS 6.0","OS":"centos"},"location":"pithos://images@okeanos.grnet.gr/pithos/centos-6.0-8-x86_64.diskdump","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"8f06e736-d890-4aed-8448-aa0e978695a9","size":628834304,"metadata":{"values":{"kernel":"2.6.32","osfamily":"linux","users":"root","gui":"No GUI","sortorder":"6","size":"601","os":"centos","root_partition":"1","description":"CentOS 6.0","OS":"centos"}},"OS":"centos","description":"CentOS 6.0","kernel":"2.6.32","GUI":""},{"status":"available","name":"Fedora","checksum":"407c7fa05c14937e213503783149aa74ad9bcfc8783ccb653a419fb86bffe0d9","created_at":"2012-03-28 13:52:45","disk_format":"diskdump","updated_at":"2012-03-28 16:56:32","properties":{"kernel":"3.1.9","osfamily":"linux","users":"root user","gui":"GNOME 3.2","sortorder":"5","size":"2641","os":"fedora","root_partition":"1","description":"Fedora 16 Desktop Edition","OS":"fedora"},"location":"debian_base-6.0-7-x86_64","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"1ba3666b-6e57-4d52-813b-e2a33185d12d","size":2765684736,"metadata":{"values":{"kernel":"3.1.9","osfamily":"linux","users":"root user","gui":"GNOME 3.2","sortorder":"5","size":"2641","os":"fedora","root_partition":"1","description":"Fedora 16 Desktop Edition","OS":"fedora"}},"OS":"fedora","description":"Fedora 16 Desktop Edition","kernel":"3.1.9","GUI":""},{"status":"available","name":"Kubuntu","checksum":"a149289f512d70c8f9f6acb0636d2ea9a5b5c3ec0b83e4398aed4a5678da6848","created_at":"2012-03-28 15:05:52","disk_format":"diskdump","updated_at":"2012-03-28 16:56:31","properties":{"kernel":"3.0.0","osfamily":"linux","users":"user","gui":"KDE 4.7.4","sortorder":"4","size":"2850","os":"kubuntu","root_partition":"1","description":"Kubuntu 11.10","OS":"kubuntu"},"location":"pithos://images@okeanos.grnet.gr/pithos/kubuntu-11.10-1-x86_64.diskdump","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"79d24739-af8f-436b-8f6e-eb2d908e0b7e","size":2985041920,"metadata":{"values":{"kernel":"3.0.0","osfamily":"linux","users":"user","gui":"KDE 4.7.4","sortorder":"4","size":"2850","os":"kubuntu","root_partition":"1","description":"Kubuntu 11.10","OS":"kubuntu"}},"OS":"kubuntu","description":"Kubuntu 11.10","kernel":"3.0.0","GUI":""},{"status":"available","name":"Ubuntu","checksum":"f508e1fc8d9cbbd360a9cfc3a68e475933063c77691dac652cb7a5c824791e1b","created_at":"2012-03-28 14:08:35","disk_format":"diskdump","updated_at":"2012-03-28 16:58:10","properties":{"kernel":"3.0.0","osfamily":"linux","users":"user","gui":"Unity 4.22","sortorder":"3","size":"2540","os":"ubuntu","root_partition":"1","description":"Ubuntu 11.10","OS":"ubuntu"},"location":"pithos://images@okeanos.grnet.gr/pithos/ubuntu-11.10-1-x86_64.diskdump","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"d8317451-820a-4c41-917b-665492ab0f81","size":2660171776,"metadata":{"values":{"kernel":"3.0.0","osfamily":"linux","users":"user","gui":"Unity 4.22","sortorder":"3","size":"2540","os":"ubuntu","root_partition":"1","description":"Ubuntu 11.10","OS":"ubuntu"}},"OS":"ubuntu","description":"Ubuntu 11.10","kernel":"3.0.0","GUI":""},{"status":"available","name":"Debian Desktop","checksum":"a7bea0bf6815168a281b505454cd9b2f07ffb9cad0d92e08e950a633c0f05bd2","created_at":"2012-03-28 14:55:37","disk_format":"diskdump","updated_at":"2012-04-03 15:52:09","properties":{"kernel":"2.6.32","osfamily":"linux","users":"root user","gui":"GNOME 2.30","sortorder":"2","size":"3314","os":"debian","root_partition":"1","description":"Debian Squeeze Desktop","OS":"debian"},"location":"pithos://images@okeanos.grnet.gr/pithos/debian_desktop-6.0-6-x86_64.diskdump","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"d7970a84-99b9-40be-b790-ce61001a5b9a","size":3482439680,"metadata":{"values":{"kernel":"2.6.32","osfamily":"linux","users":"root user","gui":"GNOME 2.30","sortorder":"2","size":"3314","os":"debian","root_partition":"1","description":"Debian Squeeze Desktop","OS":"debian"}},"OS":"debian","description":"Debian Squeeze Desktop","kernel":"2.6.32","GUI":""},{"status":"available","name":"Debian Base","checksum":"65352163c9842d6fbc5717437811495f163da2338c807819a1d4d7a50766e56c","created_at":"2012-03-28 13:39:38","disk_format":"diskdump","updated_at":"2012-03-28 16:56:29","properties":{"kernel":"2.6.32","osfamily":"linux","users":"root","gui":"No GUI","sortorder":"1","size":"451","os":"debian","root_partition":"1","description":"Debian Squeeze Base System","OS":"debian"},"location":"pithos://images@okeanos.grnet.gr/pithos/debian_base-6.0-7-x86_64.diskdump","container_format":"bare","owner":"images@okeanos.grnet.gr","is_public":True,"deleted_at":"","id":"49971ade-3bbc-4700-9a84-b3ca00133850","size":471891968,"metadata":{"values":{"kernel":"2.6.32","osfamily":"linux","users":"root","gui":"No GUI","sortorder":"1","size":"451","os":"debian","root_partition":"1","description":"Debian Squeeze Base System","OS":"debian"}},"OS":"debian","description":"Debian Squeeze Base System","kernel":"2.6.32","GUI":""},{"id":"20","name":"(deleted image)","size":-1,"progress":100,"status":"DELETED"},{"id":"21","name":"(deleted image)","size":-1,"progress":100,"status":"DELETED"}]
441 236223aa Kostas Papadimitriou
442 236223aa Kostas Papadimitriou
443 236223aa Kostas Papadimitriou
    def iter(self):
444 236223aa Kostas Papadimitriou
        return self.images
445 236223aa Kostas Papadimitriou
446 236223aa Kostas Papadimitriou
    def get_image(self, image_id):
447 236223aa Kostas Papadimitriou
        for i in self.images:
448 236223aa Kostas Papadimitriou
            if i['id'] == image_id:
449 236223aa Kostas Papadimitriou
                return i
450 236223aa Kostas Papadimitriou
        return None
451 236223aa Kostas Papadimitriou
452 236223aa Kostas Papadimitriou
    def close(self):
453 236223aa Kostas Papadimitriou
        pass
454 236223aa Kostas Papadimitriou
455 236223aa Kostas Papadimitriou
    def list_public(self, filters, params):
456 236223aa Kostas Papadimitriou
        return self.images
457 236223aa Kostas Papadimitriou
458 236223aa Kostas Papadimitriou
459 236223aa Kostas Papadimitriou
ImageBackend = PithosImageBackend
460 236223aa Kostas Papadimitriou
ImageBackend = DummyImageBackend