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

b/snf-cyclades-app/synnefo/plankton/backend.py
100 100

  
101 101
class ImageBackend(object):
102 102
    """A wrapper arround the pithos backend to simplify image handling."""
103
    
103

  
104 104
    def __init__(self, user):
105 105
        self.user = user
106 106

  
......
112 112
    def _get_image(self, location):
113 113
        def format_timestamp(t):
114 114
            return strftime('%Y-%m-%d %H:%M:%S', gmtime(t))
115
        
115

  
116 116
        account, container, object = split_location(location)
117
        
117

  
118 118
        try:
119 119
            versions = self.backend.list_versions(self.user, account,
120 120
                    container, object)
121 121
        except NameError:
122 122
            return None
123
        
123

  
124 124
        image = {}
125
        
125

  
126 126
        meta = self._get_meta(location)
127 127
        if meta:
128 128
            image['deleted_at'] = ''
......
131 131
            version, timestamp = versions[-1]
132 132
            meta = self._get_meta(location, version)
133 133
            image['deleted_at'] = format_timestamp(timestamp)
134
        
134

  
135 135
        if PLANKTON_PREFIX + 'name' not in meta:
136 136
            return None     # Not a Plankton image
137
        
137

  
138 138
        permissions = self._get_permissions(location)
139
        
139

  
140 140
        image['checksum'] = meta['hash']
141 141
        image['created_at'] = format_timestamp(versions[0][1])
142 142
        image['id'] = meta['uuid']
......
147 147
        image['store'] = 'pithos'
148 148
        image['updated_at'] = format_timestamp(meta['modified'])
149 149
        image['properties'] = {}
150
        
150

  
151 151
        for key, val in meta.items():
152 152
            if not key.startswith(PLANKTON_PREFIX):
153 153
                continue
......
156 156
                val = json.loads(val)
157 157
            if key in PLANKTON_META:
158 158
                image[key] = val
159
        
159

  
160 160
        return image
161
    
161

  
162 162
    def _get_meta(self, location, version=None):
163 163
        account, container, object = split_location(location)
164 164
        try:
......
166 166
                    object, PLANKTON_DOMAIN, version)
167 167
        except NameError:
168 168
            return None
169
    
169

  
170 170
    def _get_permissions(self, location):
171 171
        account, container, object = split_location(location)
172 172
        action, path, permissions = self.backend.get_object_permissions(
173 173
                self.user, account, container, object)
174 174
        return permissions
175
    
175

  
176 176
    def _store(self, f, size=None):
177 177
        """Breaks data into blocks and stores them in the backend"""
178
        
178

  
179 179
        bytes = 0
180 180
        hashmap = []
181 181
        backend = self.backend
182 182
        blocksize = backend.block_size
183
        
183

  
184 184
        data = f.read(blocksize)
185 185
        while data:
186 186
            hash = backend.put_block(data)
187 187
            hashmap.append(hash)
188 188
            bytes += len(data)
189 189
            data = f.read(blocksize)
190
        
190

  
191 191
        if size and size != bytes:
192 192
            raise BackendException("Invalid size")
193
        
193

  
194 194
        return hashmap, bytes
195
    
195

  
196 196
    def _update(self, location, size, hashmap, meta, permissions):
197 197
        account, container, object = split_location(location)
198 198
        self.backend.update_object_hashmap(self.user, account, container,
199 199
                object, size, hashmap, '', PLANKTON_DOMAIN,
200 200
                permissions=permissions)
201 201
        self._update_meta(location, meta, replace=True)
202
    
202

  
203 203
    def _update_meta(self, location, meta, replace=False):
204 204
        account, container, object = split_location(location)
205
        
205

  
206 206
        prefixed = {}
207 207
        for key, val in meta.items():
208 208
            if key == 'properties':
209 209
                val = json.dumps(val)
210 210
            if key in PLANKTON_META:
211 211
                prefixed[PLANKTON_PREFIX + key] = val
212
        
212

  
213 213
        self.backend.update_object_meta(self.user, account, container, object,
214 214
                PLANKTON_DOMAIN, prefixed, replace)
215
    
215

  
216 216
    def _update_permissions(self, location, permissions):
217 217
        account, container, object = split_location(location)
218 218
        self.backend.update_object_permissions(self.user, account, container,
219 219
                object, permissions)
220
    
220

  
221 221
    def add_user(self, image_id, user):
222 222
        image = self.get_image(image_id)
223 223
        assert image, "Image not found"
224
        
224

  
225 225
        location = image['location']
226 226
        permissions = self._get_permissions(location)
227 227
        read = set(permissions.get('read', []))
228 228
        read.add(user)
229 229
        permissions['read'] = list(read)
230 230
        self._update_permissions(location, permissions)
231
    
231

  
232 232
    def close(self):
233 233
        self.backend.close()
234
    
234

  
235 235
    def delete(self, image_id):
236 236
        image = self.get_image(image_id)
237 237
        account, container, object = split_location(image['location'])
238 238
        self.backend.delete_object(self.user, account, container, object)
239
    
239

  
240 240
    def get_data(self, location):
241 241
        account, container, object = split_location(location)
242 242
        size, hashmap = self.backend.get_object_hashmap(self.user, account,
......
244 244
        data = ''.join(self.backend.get_block(hash) for hash in hashmap)
245 245
        assert len(data) == size
246 246
        return data
247
    
247

  
248 248
    def get_image(self, image_id):
249 249
        try:
250 250
            account, container, object = self.backend.get_uuid(self.user,
251 251
                    image_id)
252 252
        except NameError:
253 253
            return None
254
        
254

  
255 255
        location = get_location(account, container, object)
256 256
        return self._get_image(location)
257
    
257

  
258 258
    def iter(self):
259 259
        """Iter over all images available to the user"""
260
        
260

  
261 261
        backend = self.backend
262 262
        for account in backend.list_accounts(self.user):
263 263
            for container in backend.list_containers(self.user, account,
......
268 268
                    image = self._get_image(location)
269 269
                    if image:
270 270
                        yield image
271
    
271

  
272 272
    def iter_public(self, filters=None):
273 273
        filters = filters or {}
274 274
        backend = self.backend
275
        
275

  
276 276
        keys = [PLANKTON_PREFIX + 'name']
277 277
        size_range = (None, None)
278
        
278

  
279 279
        for key, val in filters.items():
280 280
            if key == 'size_min':
281 281
                size_range = (int(val), size_range[1])
......
283 283
                size_range = (size_range[0], int(val))
284 284
            else:
285 285
                keys.append('%s = %s' % (PLANKTON_PREFIX + key, val))
286
        
286

  
287 287
        for account in backend.list_accounts(None):
288 288
            for container in backend.list_containers(None, account,
289 289
                                                     shared=True):
......
294 294
                    image = self._get_image(location)
295 295
                    if image:
296 296
                        yield image
297
    
297

  
298 298
    def iter_shared(self, member):
299 299
        """Iterate over image ids shared to this member"""
300
        
300

  
301 301
        backend = self.backend
302
        
302

  
303 303
        # To get the list we connect as member and get the list shared by us
304 304
        for container in  backend.list_containers(member, self.user):
305 305
            for object, version_id in backend.list_objects(member, self.user,
......
312 312
                        yield meta['uuid']
313 313
                except (NameError, NotAllowedError):
314 314
                    continue
315
    
315

  
316 316
    def list(self):
317 317
        """Iter over all images available to the user"""
318
        
318

  
319 319
        return list(self.iter())
320
    
320

  
321 321
    def list_public(self, filters, params):
322 322
        images = list(self.iter_public(filters))
323 323
        key = itemgetter(params.get('sort_key', 'created_at'))
324 324
        reverse = params.get('sort_dir', 'desc') == 'desc'
325 325
        images.sort(key=key, reverse=reverse)
326 326
        return images
327
    
327

  
328 328
    def list_users(self, image_id):
329 329
        image = self.get_image(image_id)
330 330
        assert image, "Image not found"
331
        
331

  
332 332
        permissions = self._get_permissions(image['location'])
333 333
        return [user for user in permissions.get('read', []) if user != '*']
334
    
334

  
335 335
    def put(self, name, f, params):
336 336
        assert 'checksum' not in params, "Passing a checksum is not supported"
337 337
        assert 'id' not in params, "Passing an ID is not supported"
......
342 342
        assert params.setdefault('container_format',
343 343
                settings.DEFAULT_CONTAINER_FORMAT) in \
344 344
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
345
        
345

  
346 346
        container = settings.DEFAULT_PLANKTON_CONTAINER
347 347
        filename = params.pop('filename', name)
348 348
        location = 'pithos://%s/%s/%s' % (self.user, container, filename)
349 349
        is_public = params.pop('is_public', False)
350 350
        permissions = {'read': ['*']} if is_public else {}
351 351
        size = params.pop('size', None)
352
        
352

  
353 353
        hashmap, size = self._store(f, size)
354
        
354

  
355 355
        meta = {}
356 356
        meta['properties'] = params.pop('properties', {})
357 357
        meta.update(name=name, status='available', **params)
358
        
358

  
359 359
        self._update(location, size, hashmap, meta, permissions)
360 360
        return self._get_image(location)
361
    
361

  
362 362
    def register(self, name, location, params):
363 363
        assert 'id' not in params, "Passing an ID is not supported"
364 364
        assert location.startswith('pithos://'), "Invalid location"
......
369 369
        assert params.setdefault('container_format',
370 370
                settings.DEFAULT_CONTAINER_FORMAT) in \
371 371
                settings.ALLOWED_CONTAINER_FORMATS, "Invalid container_format"
372
        
373
        user = self.user
372

  
373
        # user = self.user
374 374
        account, container, object = split_location(location)
375
        
375

  
376 376
        meta = self._get_meta(location)
377 377
        assert meta, "File not found"
378
        
378

  
379 379
        size = int(params.pop('size', meta['bytes']))
380 380
        if size != meta['bytes']:
381 381
            raise BackendException("Invalid size")
382
        
382

  
383 383
        checksum = params.pop('checksum', meta['hash'])
384 384
        if checksum != meta['hash']:
385 385
            raise BackendException("Invalid checksum")
386
        
386

  
387 387
        is_public = params.pop('is_public', False)
388 388
        permissions = {'read': ['*']} if is_public else {}
389
        
389

  
390 390
        meta = {}
391 391
        meta['properties'] = params.pop('properties', {})
392 392
        meta.update(name=name, status='available', **params)
393
        
393

  
394 394
        self._update_meta(location, meta)
395 395
        self._update_permissions(location, permissions)
396 396
        return self._get_image(location)
397
    
397

  
398 398
    def remove_user(self, image_id, user):
399 399
        image = self.get_image(image_id)
400 400
        assert image, "Image not found"
401
        
401

  
402 402
        location = image['location']
403 403
        permissions = self._get_permissions(location)
404 404
        try:
......
406 406
        except ValueError:
407 407
            return      # User did not have access anyway
408 408
        self._update_permissions(location, permissions)
409
    
409

  
410 410
    def replace_users(self, image_id, users):
411 411
        image = self.get_image(image_id)
412 412
        assert image, "Image not found"
413
        
413

  
414 414
        location = image['location']
415 415
        permissions = self._get_permissions(location)
416 416
        permissions['read'] = users
417 417
        if image.get('is_public', False):
418 418
            permissions['read'].append('*')
419 419
        self._update_permissions(location, permissions)
420
    
420

  
421 421
    def update(self, image_id, params):
422 422
        image = self.get_image(image_id)
423 423
        assert image, "Image not found"
424
        
424

  
425 425
        location = image['location']
426 426
        is_public = params.pop('is_public', None)
427 427
        if is_public is not None:
......
433 433
                read.discard('*')
434 434
            permissions['read'] = list(read)
435 435
            self.backend._update_permissions(location, permissions)
436
        
436

  
437 437
        meta = {}
438 438
        meta['properties'] = params.pop('properties', {})
439 439
        meta.update(**params)
440
        
440

  
441 441
        self._update_meta(location, meta)
442 442
        return self.get_image(image_id)

Also available in: Unified diff