Revision 25c3841c pithos/lib/client.py

b/pithos/lib/client.py
69 69
        self.debug = debug
70 70
        self.token = token
71 71
    
72
    def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
73
                          blocksize=1024):
74
        
75
        http = HTTPConnection(self.host)
76
        
77
        # write header
78
        path = '/%s/%s%s' % (self.api, self.account, path)
79
        http.putrequest(method, path)
80
        http.putheader('x-auth-token', self.token)
81
        http.putheader('content-type', 'application/octet-stream')
82
        http.putheader('transfer-encoding', 'chunked')
83
        if headers:
84
            for header,value in headers.items():
85
                http.putheader(header, value)
86
        http.endheaders()
87
        
88
        # write body
89
        data = ''
90
        while True:
91
            if f.closed:
92
                break
93
            block = f.read(blocksize)
94
            if block == '':
95
                break
96
            data = '%s\r\n%s\r\n' % (hex(len(block)), block)
97
            try:
98
                http.send(data)
99
            except:
100
                #retry
101
                http.send(data)
102
        data = '0x0\r\n'
103
        try:
104
            http.send(data)
105
        except:
106
            #retry
107
            http.send(data)
108
        
109
        # get response
110
        resp = http.getresponse()
111
        
112
        headers = dict(resp.getheaders())
113
        
114
        if self.verbose:
115
            print '%d %s' % (resp.status, resp.reason)
116
            for key, val in headers.items():
117
                print '%s: %s' % (key.capitalize(), val)
118
            print
119
        
120
        length = resp.getheader('Content-length', None)
121
        data = resp.read(length)
122
        if self.debug:
123
            print data
124
            print
125
        
126
        if int(resp.status) in ERROR_CODES.keys():
127
            raise Fault(data, int(resp.status))
128
        
129
        #print '*',  resp.status, headers, data
130
        return resp.status, headers, data
131
    
132
    def _req(self, method, path, body=None, headers=None, format='text',
133
            params=None):
72
    def _req(self, method, path, body=None, headers={}, format='text',
73
            params={}):
134 74
        full_path = '/%s/%s%s?format=%s' % (self.api, self.account, path,
135 75
                                            format)
136
        if params:
137
            for k,v in params.items():
138
                if v:
139
                    full_path = '%s&%s=%s' %(full_path, k, v)
140
                else:
141
                    full_path = '%s&%s' %(full_path, k)
76
        for k,v in params.items():
77
            if v:
78
                full_path = '%s&%s=%s' %(full_path, k, v)
79
            else:
80
                full_path = '%s&%s' %(full_path, k)
142 81
        conn = HTTPConnection(self.host)
143 82
        
144 83
        #encode whitespace
145 84
        full_path = full_path.replace(' ', '%20')
146 85
        
147 86
        kwargs = {}
148
        kwargs['headers'] = headers or {}
87
        for k,v in headers.items():
88
            headers.pop(k)
89
            k = k.replace('_', '-')
90
            headers[k] = v
91
        
92
        kwargs['headers'] = headers
149 93
        kwargs['headers']['X-Auth-Token'] = self.token
150
        if not headers or \
151
        'transfer-encoding' not in headers \
152
        or headers['transfer-encoding'] != 'chunked':
153
            kwargs['headers']['content-length'] = len(body) if body else 0
154 94
        if body:
155 95
            kwargs['body'] = body
156 96
        else:
157 97
            kwargs['headers']['content-type'] = ''
98
        kwargs['headers'].setdefault('content-length', len(body) if body else 0)
158 99
        kwargs['headers'].setdefault('content-type', 'application/octet-stream')
159 100
        try:
160 101
            #print '*', method, full_path, kwargs
......
183 124
        #print '*',  resp.status, headers, data
184 125
        return resp.status, headers, data
185 126
    
186
    def delete(self, path, format='text'):
187
        return self._req('DELETE', path, format=format)
127
    def delete(self, path, format='text', params={}):
128
        return self._req('DELETE', path, format=format, params=params)
188 129
    
189
    def get(self, path, format='text', headers=None, params=None):
130
    def get(self, path, format='text', headers=None, params={}):
190 131
        return self._req('GET', path, headers=headers, format=format,
191 132
                        params=params)
192 133
    
193
    def head(self, path, format='text', params=None):
134
    def head(self, path, format='text', params={}):
194 135
        return self._req('HEAD', path, format=format, params=params)
195 136
    
196
    def post(self, path, body=None, format='text', headers=None, params=None):
137
    def post(self, path, body=None, format='text', headers=None, params={}):
197 138
        return self._req('POST', path, body, headers=headers, format=format,
198 139
                        params=params)
199 140
    
200 141
    def put(self, path, body=None, format='text', headers=None):
201 142
        return self._req('PUT', path, body, headers=headers, format=format)
202 143
    
203
    def _list(self, path, detail=False, params=None, headers=None):
144
    def _list(self, path, detail=False, params={}, **headers):
204 145
        format = 'json' if detail else 'text'
205 146
        status, headers, data = self.get(path, format=format, headers=headers,
206 147
                                         params=params)
207 148
        if detail:
208 149
            data = json.loads(data) if data else ''
209 150
        else:
210
            data = data.strip().split('\n')
151
            data = data.strip().split('\n') if data else ''
211 152
        return data
212 153
    
213
    def _get_metadata(self, path, prefix=None, params=None):
154
    def _get_metadata(self, path, prefix=None, params={}):
214 155
        status, headers, data = self.head(path, params=params)
215 156
        prefixlen = len(prefix) if prefix else 0
216 157
        meta = {}
......
222 163
            meta[key] = val
223 164
        return meta
224 165
    
225
    def _update_metadata(self, path, entity, **meta):
166
    def _filter(self, l, d):
226 167
        """
227
        adds new and updates the values of previously set metadata
168
        filter out from l elements having the metadata values provided
228 169
        """
229
        params = {'update':None}
170
        ll = l
171
        for elem in l:
172
            if type(elem) == types.DictionaryType:
173
                for key in d.keys():
174
                    k = 'x_object_meta_%s' % key
175
                    if k in elem.keys() and elem[k] == d[key]:
176
                        ll.remove(elem)
177
                        break
178
        return ll
179
    
180
class OOS_Client(Client):
181
    """Openstack Objest Storage Client"""
182
    
183
    def _update_metadata(self, path, entity, **meta):
184
        """adds new and updates the values of previously set metadata"""
185
        ex_meta = self.retrieve_account_metadata(restricted=True)
186
        ex_meta.update(meta)
230 187
        headers = {}
231 188
        prefix = 'x-%s-meta-' % entity
232
        for k,v in meta.items():
189
        for k,v in ex_meta.items():
233 190
            k = '%s%s' % (prefix, k)
234 191
            headers[k] = v
235 192
        return self.post(path, headers=headers, params=params)
236 193
    
237 194
    def _delete_metadata(self, path, entity, meta=[]):
238
        """
239
        delete previously set metadata
240
        """
241
        params = {'update':None}
195
        """delete previously set metadata"""
196
        ex_meta = self.retrieve_account_metadata(restricted=True)
242 197
        headers = {}
243 198
        prefix = 'x-%s-meta-' % entity
244
        for m in meta:
245
            headers['%s%s' % (prefix, m)] = None
199
        for k in ex_meta.keys():
200
            if k in meta:
201
                headers['%s%s' % (prefix, k)] = ex_meta[k]
246 202
        return self.post(path, headers=headers)
247 203
    
248 204
    # Storage Account Services
249 205
    
250
    def list_containers(self, detail=False, params=None, headers=None):
251
        return self._list('', detail, params, headers)
206
    def list_containers(self, detail=False, limit=10000, marker=None, params={},
207
                        **headers):
208
        """lists containers"""
209
        if not params:
210
            params = {}
211
        params.update({'limit':limit, 'marker':marker})
212
        return self._list('', detail, params, **headers)
252 213
    
253
    def account_metadata(self, restricted=False, until=None):
214
    def retrieve_account_metadata(self, restricted=False, **params):
215
        """returns the account metadata"""
254 216
        prefix = 'x-account-meta-' if restricted else None
255
        params = {'until':until} if until else None
256
        return self._get_metadata('', prefix, params=params)
217
        return self._get_metadata('', prefix, params)
257 218
    
258 219
    def update_account_metadata(self, **meta):
220
        """updates the account metadata"""
259 221
        return self._update_metadata('', 'account', **meta)
260 222
        
261 223
    def delete_account_metadata(self, meta=[]):
224
        """deletes the account metadata"""
262 225
        return self._delete_metadata('', 'account', meta)
263 226
    
264
    def set_account_groups(self, **groups):
265
        """
266
        create account groups
267
        """
268
        headers = {}
269
        for key, val in groups.items():
270
            headers['x-account-group-%s' % key] = val
271
        params = {'update':None}
272
        return self.post('', headers=headers, params=params)
273
    
274
    def unset_account_groups(self, groups=[]):
275
        """
276
        delete account groups
277
        """
278
        headers = {}
279
        for elem in groups:
280
            headers['x-account-group-%s' % elem] = ''
281
        params = {'update':None}
282
        return self.post('', headers=headers, params=params)
283
    
284 227
    # Storage Container Services
285 228
    
286
    def _filter(self, l, d):
287
        """
288
        filter out from l elements having the metadata values provided
289
        """
290
        ll = l
291
        for elem in l:
292
            if type(elem) == types.DictionaryType:
293
                for key in d.keys():
294
                    k = 'x_object_meta_%s' % key
295
                    if k in elem.keys() and elem[k] == d[key]:
296
                        ll.remove(elem)
297
                        break
298
        return ll
299
    
300 229
    def _filter_trashed(self, l):
301 230
        return self._filter(l, {'trash':'true'})
302 231
    
303
    def list_objects(self, container, detail=False, headers=None,
304
                     include_trashed=False, **params):
305
        l = self._list('/' + container, detail, params, headers)
232
    def list_objects(self, container, detail=False, limit=10000, marker=None,
233
                     prefix=None, delimiter=None, path=None,
234
                     include_trashed=False, params={}, **headers):
235
        """returns a list with the container objects"""
236
        params.update({'limit':limit, 'marker':marker, 'prefix':prefix,
237
                       'delimiter':delimiter, 'path':path})
238
        l = self._list('/' + container, detail, params, **headers)
306 239
        if not include_trashed:
307 240
            l = self._filter_trashed(l)
308 241
        return l
309 242
    
310
    def create_container(self, container, headers=None, **meta):
243
    def create_container(self, container, **meta):
244
        """creates a container"""
245
        headers = {}
311 246
        for k,v in meta.items():
312 247
            headers['x-container-meta-%s' %k.strip().upper()] = v.strip()
313 248
        status, header, data = self.put('/' + container, headers=headers)
......
317 252
            raise Fault(data, int(status))
318 253
        return True
319 254
    
320
    def delete_container(self, container):
321
        return self.delete('/' + container)
255
    def delete_container(self, container, params={}):
256
        """deletes a container"""
257
        return self.delete('/' + container, params=params)
322 258
    
323
    def retrieve_container_metadata(self, container, restricted=False,
324
                                    until=None):
259
    def retrieve_container_metadata(self, container, restricted=False, **params):
260
        """returns the container metadata"""
325 261
        prefix = 'x-container-meta-' if restricted else None
326
        params = {'until':until} if until else None
327
        return self._get_metadata('/%s' % container, prefix, params=params)
262
        return self._get_metadata('/%s' % container, prefix, params)
328 263
    
329 264
    def update_container_metadata(self, container, **meta):
265
        """unpdates the container metadata"""
330 266
        return self._update_metadata('/' + container, 'container', **meta)
331 267
        
332 268
    def delete_container_metadata(self, container, meta=[]):
269
        """deletes the container metadata"""
333 270
        path = '/%s' % (container)
334 271
        return self._delete_metadata(path, 'container', meta)
335 272
    
336
    def set_container_policies(self, container, **policies):
337
        path = '/%s' % (container)
338
        headers = {}
339
        print ''
340
        for key, val in policies.items():
341
            headers['x-container-policy-%s' % key] = val
342
        return self.post(path, headers=headers)
343
    
344 273
    # Storage Object Services
345 274
    
346
    def retrieve_object(self, container, object, detail=False, headers=None,
347
                        version=None):
275
    def request_object(self, container, object, detail=False, params={},
276
                        **headers):
277
        """returns tuple containing the status, headers and data response for an object request"""
348 278
        path = '/%s/%s' % (container, object)
349
        format = 'json' if detail else 'text'
350
        params = {'version':version} if version else None 
279
        format = 'json' if detail else 'text' 
351 280
        status, headers, data = self.get(path, format, headers, params)
352
        return data
281
        return status, headers, data
282
    
283
    def retrieve_object(self, container, object, detail=False, params={},
284
                             **headers):
285
        """returns an object's data"""
286
        t = self.request_object(container, object, detail, params, **headers)
287
        return t[2]
353 288
    
354 289
    def create_directory_marker(self, container, object):
290
        """creates a dierectory marker"""
355 291
        if not object:
356 292
            raise Fault('Directory markers have to be nested in a container')
357 293
        h = {'Content-Type':'application/directory'}
358
        return self.create_object(container, object, f=None, headers=h)
359
    
360
    def _set_public_header(self, headers, public=False):
361
        """
362
        sets the public header
363
        """
364
        if public == None:
365
            return
366
        elif public:
367
            headers['x-object-public'] = public
368
        else:
369
            headers['x-object-public'] = ''
370
    
371
    def create_object(self, container, object, f=stdin, chunked=False,
372
                      blocksize=1024, headers={}, use_hashes=False,
373
                      public=None, **meta):
374
        """
375
        creates an object
376
        if f is None then creates a zero length object
377
        if f is stdin or chunked is set then performs chunked transfer 
378
        """
379
        path = '/%s/%s' % (container, object)
380
        for k,v in meta.items():
381
            headers['x-object-meta-%s' %k.strip().upper()] = v.strip()
382
        self._set_public_header(headers, public)
383
        headers = headers if headers else None
384
        if not chunked:
385
            format = 'json' if use_hashes else 'text'
386
            data = f.read() if f else None
387
            if data:
388
                if format == 'json':
389
                    try:
390
                        data = eval(data)
391
                        data = json.dumps(data)
392
                    except SyntaxError:
393
                        raise Fault('Invalid formatting')
394
            return self.put(path, data, headers=headers, format=format)
395
        else:
396
            return self._chunked_transfer(path, 'PUT', f, headers=headers,
397
                                   blocksize=1024)
294
        return self.create_zero_length_object(container, object, **h)
398 295
    
399
    def update_object_data(self, container, object, data=None, headers={},
400
                      offset=None, public=None, **meta):
296
    def create_object(self, container, object, f=stdin, format='text', meta={},
297
                      etag=None, content_type=None, content_encoding=None,
298
                      content_disposition=None, **headers):
299
        """creates an object"""
401 300
        path = '/%s/%s' % (container, object)
301
        for k, v  in headers.items():
302
            if not v:
303
                headers.pop(k)
304
        
305
        l = ['etag', 'content_encoding', 'content_disposition', 'content_type']
306
        l = [elem for elem in l if eval(elem)]
307
        for elem in l:
308
            headers.update({elem:eval(elem)})
309
            
402 310
        for k,v in meta.items():
403 311
            headers['x-object-meta-%s' %k.strip()] = v.strip()
404
        if 'content-range' not in headers.keys():
405
            if offset:
406
                headers['content-range'] = 'bytes %s-/*' % offset
407
            else:
408
                headers['content-range'] = 'bytes */*'
409
        self._set_public_header(headers, public)
410
        headers = headers if headers else None
411
        return self.post(path, data, headers=headers)
312
        data = f.read() if f else None
313
        return self.put(path, data, format, headers=headers)
412 314
    
413
    def update_object(self, container, object, f=stdin, chunked=False,
414
                      blocksize=1024, headers={}, offset=None, public=None,
415
                      **meta):
315
    def update_object(self, container, object, f=stdin, offset=None, meta={},
316
                      content_length=None, content_type=None,
317
                      content_encoding=None, content_disposition=None,
318
                      **headers):
416 319
        path = '/%s/%s' % (container, object)
320
        for k, v  in headers.items():
321
            if not v:
322
                headers.pop(k)
323
        
324
        l = ['content_encoding', 'content_disposition', 'content_type',
325
             'content_length']
326
        l = [elem for elem in l if eval(elem)]
327
        for elem in l:
328
            headers.update({elem:eval(elem)})
329
            
330
        if 'content_range' not in headers.keys():
331
            if offset != None:
332
                headers['content_range'] = 'bytes %s-/*' % offset
333
            else:
334
                headers['content_range'] = 'bytes */*'
335
            
417 336
        for k,v in meta.items():
418 337
            headers['x-object-meta-%s' %k.strip()] = v.strip()
419
        if offset:
420
            headers['content-range'] = 'bytes %s-/*' % offset
421
        else:
422
            headers['content-range'] = 'bytes */*'
423
        self._set_public_header(headers, public)
424
        headers = headers if headers else None
425
        if not chunked and f != stdin:
426
            data = f.read() if f else None
427
            return self.post(path, data, headers=headers)
428
        else:
429
            return self._chunked_transfer(path, 'POST', f, headers=headers,
430
                                   blocksize=1024)
338
        data = f.read() if f else None
339
        return self.post(path, data, headers=headers)
431 340
    
432 341
    def _change_obj_location(self, src_container, src_object, dst_container,
433 342
                             dst_object, remove=False, public=False, **meta):
......
456 365
                                   dst_container, dst_object, True,
457 366
                                   public, **meta)
458 367
    
459
    def delete_object(self, container, object):
460
        return self.delete('/%s/%s' % (container, object))
368
    def delete_object(self, container, object, params={}):
369
        return self.delete('/%s/%s' % (container, object), params=params)
461 370
    
462 371
    def retrieve_object_metadata(self, container, object, restricted=False,
463 372
                                 version=None):
......
466 375
        """
467 376
        path = '/%s/%s' % (container, object)
468 377
        prefix = 'x-object-meta-' if restricted else None
469
        params = {'version':version} if version else None
378
        params = {'version':version} if version else {}
470 379
        return self._get_metadata(path, prefix, params=params)
471 380
    
472 381
    def update_object_metadata(self, container, object, **meta):
......
477 386
        path = '/%s/%s' % (container, object)
478 387
        return self._delete_metadata(path, 'object', meta)
479 388
    
480
    def trash_object(self, container, object):
389
class Pithos_Client(OOS_Client):
390
    """Pithos Storage Client. Extends OOS_Client"""
391
    
392
    def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
393
                          blocksize=1024):
394
        """perfomrs a chunked request"""
395
        http = HTTPConnection(self.host)
396
        
397
        # write header
398
        path = '/%s/%s%s' % (self.api, self.account, path)
399
        http.putrequest(method, path)
400
        http.putheader('x-auth-token', self.token)
401
        http.putheader('content-type', 'application/octet-stream')
402
        http.putheader('transfer-encoding', 'chunked')
403
        if headers:
404
            for header,value in headers.items():
405
                http.putheader(header, value)
406
        http.endheaders()
407
        
408
        # write body
409
        data = ''
410
        while True:
411
            if f.closed:
412
                break
413
            block = f.read(blocksize)
414
            if block == '':
415
                break
416
            data = '%s\r\n%s\r\n' % (hex(len(block)), block)
417
            try:
418
                http.send(data)
419
            except:
420
                #retry
421
                http.send(data)
422
        data = '0x0\r\n'
423
        try:
424
            http.send(data)
425
        except:
426
            #retry
427
            http.send(data)
428
        
429
        # get response
430
        resp = http.getresponse()
431
        
432
        headers = dict(resp.getheaders())
433
        
434
        if self.verbose:
435
            print '%d %s' % (resp.status, resp.reason)
436
            for key, val in headers.items():
437
                print '%s: %s' % (key.capitalize(), val)
438
            print
439
        
440
        length = resp.getheader('Content-length', None)
441
        data = resp.read(length)
442
        if self.debug:
443
            print data
444
            print
445
        
446
        if int(resp.status) in ERROR_CODES.keys():
447
            raise Fault(data, int(resp.status))
448
        
449
        #print '*',  resp.status, headers, data
450
        return resp.status, headers, data
451
    
452
    def _update_metadata(self, path, entity, **meta):
453
        """
454
        adds new and updates the values of previously set metadata
455
        """
456
        params = {'update':None}
457
        headers = {}
458
        prefix = 'x-%s-meta-' % entity
459
        for k,v in meta.items():
460
            k = '%s%s' % (prefix, k)
461
            headers[k] = v
462
        return self.post(path, headers=headers, params=params)
463
    
464
    def _delete_metadata(self, path, entity, meta=[]):
481 465
        """
482
        trashes an object
483
        actually resets all object metadata with trash = true 
466
        delete previously set metadata
484 467
        """
468
        params = {'update':None}
469
        headers = {}
470
        prefix = 'x-%s-meta-' % entity
471
        for m in meta:
472
            headers['%s%s' % (prefix, m)] = None
473
        return self.post(path, headers=headers, params=params)
474
    
475
    # Storage Account Services
476
    
477
    def list_containers(self, detail=False, if_modified_since=None,
478
                        if_unmodified_since=None, limit=1000, marker=None,
479
                        until=None):
480
        """returns a list with the account containers"""
481
        params = {'until':until} if until else None
482
        headers = {'if-modified-since':if_modified_since,
483
                   'if-unmodified-since':if_unmodified_since}
484
        return OOS_Client.list_containers(self, detail=detail, limit=limit,
485
                                          marker=marker, params=params,
486
                                          **headers)
487
    
488
    def retrieve_account_metadata(self, restricted=False, until=None):
489
        """returns the account metadata"""
490
        params = {'until':until} if until else {}
491
        return OOS_Client.retrieve_account_metadata(self, restricted=restricted,
492
                                                   **params)
493
    
494
    def set_account_groups(self, **groups):
495
        """create account groups"""
496
        headers = {}
497
        for key, val in groups.items():
498
            headers['x-account-group-%s' % key] = val
499
        params = {'update':None}
500
        return self.post('', headers=headers, params=params)
501
    
502
    def unset_account_groups(self, groups=[]):
503
        """delete account groups"""
504
        headers = {}
505
        for elem in groups:
506
            headers['x-account-group-%s' % elem] = ''
507
        params = {'update':None}
508
        return self.post('', headers=headers, params=params)
509
    
510
    # Storage Container Services
511
    
512
    def list_objects(self, container, detail=False, limit=10000, marker=None,
513
                     prefix=None, delimiter=None, path=None,
514
                     include_trashed=False, params={}, if_modified_since=None,
515
                     if_unmodified_since=None, meta={}, until=None):
516
        """returns a list with the container objects"""
517
        params = {'until':until, 'meta':meta}
518
        args = locals()
519
        for elem in ['self', 'container', 'params', 'until', 'meta']:
520
            args.pop(elem)
521
        return OOS_Client.list_objects(self, container, params=params, 
522
                                       **args)
523
    
524
    def retrieve_container_metadata(self, container, restricted=False,
525
                                    until=None):
526
        """returns container's metadata"""
527
        params = {'until':until} if until else {}
528
        return OOS_Client.retrieve_container_metadata(self, container,
529
                                                      restricted=restricted,
530
                                                      **params)
531
    
532
    def set_container_policies(self, container, **policies):
533
        """sets containers policies"""
534
        path = '/%s' % (container)
535
        headers = {}
536
        print ''
537
        for key, val in policies.items():
538
            headers['x-container-policy-%s' % key] = val
539
        return self.post(path, headers=headers)
540
    
541
    def delete_container(self, container, until=None):
542
        """deletes a container or the container history until the date provided"""
543
        params = {'until':until} if until else {}
544
        return OOS_Client.delete_container(self, container, params)
545
    
546
    # Storage Object Services
547
    
548
    def retrieve_object(self, container, object, params={}, detail=False, range=None,
549
                        if_range=None, if_match=None, if_none_match=None,
550
                        if_modified_since=None, if_unmodified_since=None,
551
                        **headers):
552
        """returns an object"""
553
        headers={}
554
        l = ['range', 'if_range', 'if_match', 'if_none_match',
555
             'if_modified_since', 'if_unmodified_since']
556
        l = [elem for elem in l if eval(elem)]
557
        for elem in l:
558
            headers.update({elem:eval(elem)})
559
        return OOS_Client.retrieve_object(self, container, object, detail=detail,
560
                                          params=params, **headers)
561
    
562
    def retrieve_object_version(self, container, object, version, detail=False,
563
                                range=None, if_range=None, if_match=None,
564
                                if_none_match=None, if_modified_since=None,
565
                                if_unmodified_since=None):
566
        """returns a specific object version"""
567
        args = locals()
568
        l = ['self', 'container', 'object']
569
        for elem in l:
570
            args.pop(elem)
571
        params = {'version':version}
572
        return self.retrieve_object(container, object, params, **args)
573
    
574
    def retrieve_object_versionlist(self, container, object, range=None,
575
                                    if_range=None, if_match=None,
576
                                    if_none_match=None, if_modified_since=None,
577
                                    if_unmodified_since=None):
578
        """returns the object version list"""
579
        args = locals()
580
        l = ['self', 'container', 'object']
581
        for elem in l:
582
            args.pop(elem)
583
            
584
        return self.retrieve_object_version(container, object, version='list',
585
                                            detail=True, **args)
586
    
587
    def create_object(self, container, object, f=stdin, meta={},
588
                      etag=None, content_type=None, content_encoding=None,
589
                      content_disposition=None, x_object_manifest=None,
590
                      x_object_sharing=None, x_object_public=None):
591
        """creates an object"""
592
        args = locals()
593
        for elem in ['self', 'container', 'object']:
594
            args.pop(elem)
595
        return OOS_Client.create_object(self, container, object, **args)
596
        
597
    def create_object_using_chunks(self, container, object, f=stdin,
598
                                    blocksize=1024, meta={}, etag=None,
599
                                    content_type=None, content_encoding=None,
600
                                    content_disposition=None, 
601
                                    x_object_sharing=None,
602
                                    x_object_manifest=None, 
603
                                    x_object_public=None):
604
        """creates an object (incremental upload)"""
605
        path = '/%s/%s' % (container, object)
606
        headers = {}
607
        l = ['etag', 'content_type', 'content_encoding', 'content_disposition', 
608
             'x_object_sharing', 'x_object_manifest', 'x_object_public']
609
        l = [elem for elem in l if eval(elem)]
610
        for elem in l:
611
            headers.update({elem:eval(elem)})
612
        
613
        for k,v in meta.items():
614
            headers['x-object-meta-%s' %k.strip()] = v.strip()
615
        
616
        return self._chunked_transfer(path, 'PUT', f, headers=headers,
617
                                      blocksize=blocksize)
618
    
619
    def create_object_by_hashmap(container, object, f=stdin, format='json',
620
                                 meta={}, etag=None, content_encoding=None,
621
                                 content_disposition=None, content_type=None,
622
                                 x_object_sharing=None, x_object_manifest=None,
623
                                 x_object_public = None):
624
        """creates an object by uploading hashes representing data instead of data"""
625
        args = locals()
626
        for elem in ['self', 'container', 'object']:
627
            args.pop(elem)
628
            
629
        data = f.read() if f else None
630
        if data and format == 'json':
631
            try:
632
                data = eval(data)
633
                data = json.dumps(data)
634
            except SyntaxError:
635
                raise Fault('Invalid formatting')
636
        
637
        #TODO check with xml
638
        return self.create_object(container, object, **args)
639
    
640
    def create_manifestation(self, container, object, manifest):
641
        """creates a manifestation"""
642
        headers={'x_object_manifest':manifest}
643
        return self.create_object(container, object, f=None, **headers)
644
    
645
    def update_object(self, container, object, f=stdin, offset=None, meta={},
646
                      content_length=None, content_type=None, content_range=None,
647
                      content_encoding=None, content_disposition=None,
648
                      x_object_bytes=None, x_object_manifest=None,
649
                      x_object_sharing=None, x_object_public=None):
650
        """updates an object"""
651
        spath = '/%s/%s' % (container, object)
652
        args = locals()
653
        for elem in ['self', 'container', 'object']:
654
            args.pop(elem)
655
        
656
        return OOS_Client.update_object(self, container, object, **args)
657
        
658
    def update_object_using_chunks(self, container, object, f=stdin,
659
                                    blocksize=1024, offset=None, meta={},
660
                                    content_type=None, content_encoding=None,
661
                                    content_disposition=None, x_object_bytes=None,
662
                                    x_object_manifest=None, x_object_sharing=None,
663
                                    x_object_public=None):
664
        """updates an object (incremental upload)"""
665
        path = '/%s/%s' % (container, object)
666
        headers = {}
667
        l = ['content_type', 'content_encoding', 'content_disposition',
668
             'x_object_bytes', 'x_object_manifest', 'x_object_sharing',
669
             'x_object_public']
670
        l = [elem for elem in l if eval(elem)]
671
        for elem in l:
672
            headers.update({elem:eval(elem)})
673
        
674
        if offset != None:
675
            headers['content_range'] = 'bytes %s-/*' % offset
676
        else:
677
            headers['content_range'] = 'bytes */*'
678
        
679
        for k,v in meta.items():
680
            headers['x-object-meta-%s' %k.strip()] = v.strip()
681
        
682
        return self._chunked_transfer(path, 'POST', f, headers=headers,
683
                                      blocksize=blocksize)
684
    
685
    def delete_object(self, container, object, until=None):
686
        """deletes an object or the object history until the date provided"""
687
        params = {'until':until} if until else {}
688
        return OOS_Client.delete_object(self, container, object, params)
689
    
690
    def trash_object(self, container, object):
691
        """trashes an object"""
485 692
        path = '/%s/%s' % (container, object)
486 693
        meta = {'trash':'true'}
487 694
        return self._update_metadata(path, 'object', **meta)
488 695
    
489 696
    def restore_object(self, container, object):
490
        """
491
        restores a trashed object
492
        actualy removes trash object metadata info
493
        """
697
        """restores a trashed object"""
494 698
        return self.delete_object_metadata(container, object, ['trash'])
495 699
    
700
    def _set_public_header(self, headers, public=False):
701
        """sets the public header"""
702
        if not headers:
703
            headers = {}
704
        if public == None:
705
            return
706
        else:
707
            headers['x-object-public'] = public if public else ''
708
    
496 709
    def publish_object(self, container, object):
497
        """
498
        sets a previously created object publicly accessible
499
        """
710
        """sets a previously created object publicly accessible"""
500 711
        path = '/%s/%s' % (container, object)
501
        headers = {'content-range':'bytes */*'}
712
        headers = {'content_range':'bytes */*'}
502 713
        self._set_public_header(headers, public=True)
503 714
        return self.post(path, headers=headers)
504 715
    
505 716
    def unpublish_object(self, container, object):
506
        """
507
        unpublish an object
508
        """
717
        """unpublish an object"""
509 718
        path = '/%s/%s' % (container, object)
510
        headers = {'content-range':'bytes */*'}
719
        headers = {'content_range':'bytes */*'}
511 720
        self._set_public_header(headers, public=False)
512 721
        return self.post(path, headers=headers)

Also available in: Unified diff