Revision 7812e1f9

b/pithos/api/tests.py
32 32
# or implied, of GRNET S.A.
33 33

  
34 34
from pithos.lib.client import Pithos_Client, Fault
35
import unittest
36 35
from django.utils import simplejson as json
37 36
from xml.dom import minidom
38 37
from StringIO import StringIO
38
import unittest
39 39
import time as _time
40 40
import types
41 41
import hashlib
......
267 267
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
268 268
        for item in self.containers:
269 269
            self.client.create_container(item)
270
        
271
                #keep track of initial account groups
272
        self.initial_groups = self.client.retrieve_account_groups()
273
        
274
        #keep track of initial account meta
275
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
276
        
277
        meta = {'foo':'bar'}
278
        self.client.update_account_metadata(**meta)
279
        self.updated_meta = self.initial_meta.update(meta)
270 280
    
271 281
    def tearDown(self):
272
        self.client.delete_account_metadata(['foo'])
282
        #delete additionally created meta
283
        l = []
284
        for m in self.client.retrieve_account_metadata(restricted=True):
285
            if m not in self.initial_meta:
286
                l.append(m)
287
        self.client.delete_account_metadata(l)
288
        
289
        #delete additionally created groups
290
        l = []
291
        for g in self.client.retrieve_account_groups():
292
            if g not in self.initial_groups:
293
                l.append(g)
294
        self.client.unset_account_groups(l)
295
        
296
        #print '#', self.client.retrieve_account_groups()
297
        #print '#', self.client.retrieve_account_metadata(restricted=True)
273 298
        BaseTestCase.tearDown(self)
274 299
    
275 300
    def test_get_account_meta(self):
......
293 318
        past = t - datetime.timedelta(minutes=-15)
294 319
        past = int(_time.mktime(past.timetuple()))
295 320
        
296
        meta = {'foo':'bar'}
321
        meta = {'premium':True}
297 322
        self.client.update_account_metadata(**meta)
298 323
        meta = self.client.retrieve_account_metadata(restricted=True,
299 324
                                                     until=past)
300
        self.assertTrue('foo' not in meta)
325
        self.assertTrue('premium' not in meta)
301 326
        
302 327
        meta = self.client.retrieve_account_metadata(restricted=True)
303
        self.assertTrue('foo' in meta)
328
        self.assertTrue('premium' in meta)
304 329
    
305 330
    def test_get_account_meta_until_invalid_date(self):
306
        meta = {'foo':'bar'}
331
        meta = {'premium':True}
307 332
        self.client.update_account_metadata(**meta)
308 333
        meta = self.client.retrieve_account_metadata(restricted=True,
309 334
                                                     until='kshfksfh')
310
        self.assertTrue('foo' in meta)
335
        self.assertTrue('premium' in meta)
311 336

  
312 337
class AccountGet(BaseTestCase):
313 338
    def setUp(self):
......
478 503
            
479 504
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
480 505
    
481
    #def test_delete_meta(self):
482
    #    with AssertMappingInvariant(self.client.reset_account_groups):
483
    #        meta = {'test':'test', 'tost':'tost'}
484
    #        self.client.update_account_metadata(**meta)
485
    #        
486
    #        self.client.delete_account_metadata(**meta)
506
    def test_delete_meta(self):
507
        with AssertMappingInvariant(self.client.retrieve_account_groups):
508
            meta = {'test':'test', 'tost':'tost'}
509
            self.client.update_account_metadata(**meta)
510
            
511
            self.client.delete_account_metadata(meta.keys())
512
            
513
            account_meta = self.client.retrieve_account_metadata(restricted=True)
514
            for m in meta:
515
                self.assertTrue(m not in account_meta.keys())
487 516
    
488 517
    def test_set_account_groups(self):
489 518
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
......
1131 1160
        #assert content-type
1132 1161
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1133 1162
    
1163
    def test_upload_with_name_containing_slash(self):
1164
        name = '/%s' % o_names[0]
1165
        meta = {'test':'test1'}
1166
        o = self.upload_random_data(self.container, name, **meta)
1167
        
1168
        self.assertEqual(o['data'],
1169
                         self.client.retrieve_object(self.container, name))
1170
        
1171
        self.assertTrue(name in self.client.list_objects(self.container))
1172
    
1173
    def test_create_directory_marker(self):
1174
        self.client.create_directory_marker(self.container, 'foo')
1175
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1176
        self.assertEqual(meta['content-length'], '0')
1177
        self.assertEqual(meta['content-type'], 'application/directory')
1178

  
1134 1179
    def test_upload_unprocessable_entity(self):
1135 1180
        meta={'etag':'123', 'test':'test1'}
1136 1181
        
......
1165 1210
                                              self.obj['name'],
1166 1211
                                              self.containers[0],
1167 1212
                                              'testcopy',
1168
                                              **meta)[0]
1213
                                              meta)[0]
1169 1214
            
1170 1215
            #assert copy success
1171 1216
            self.assertEqual(status, 201)
......
1190 1235
                                             self.obj['name'],
1191 1236
                                             self.containers[1],
1192 1237
                                             'testcopy',
1193
                                             **meta)[0]
1238
                                             meta)[0]
1194 1239
            self.assertEqual(status, 201)
1195 1240
            
1196 1241
            # assert updated metadata
......
1207 1252
        #copy from invalid object
1208 1253
        meta = {'test':'testcopy'}
1209 1254
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1210
                                 'test.py', self.containers[1], 'testcopy',
1211
                                 **meta)
1255
                                 'test.py', self.containers[1], 'testcopy', meta)
1212 1256
        
1213 1257
        #copy from invalid container
1214 1258
        meta = {'test':'testcopy'}
1215 1259
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1216 1260
                                 self.obj['name'], self.containers[1],
1217
                                 'testcopy', **meta)
1261
                                 'testcopy', meta)
1218 1262
        
1219 1263

  
1220
class ObjectMove(ObjectCopy):
1264
class ObjectMove(BaseTestCase):
1265
    def setUp(self):
1266
        BaseTestCase.setUp(self)
1267
        self.account = 'test'
1268
        self.containers = ['c1', 'c2']
1269
        for c in self.containers:
1270
            self.client.create_container(c)
1271
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1272
    
1221 1273
    def test_move(self):
1222 1274
        #perform move
1223 1275
        meta = {'test':'testcopy'}
1224 1276
        src_path = os.path.join('/', self.containers[0], self.obj['name'])
1225 1277
        status = self.client.move_object(self.containers[0], self.obj['name'],
1226 1278
                                         self.containers[0], 'testcopy',
1227
                                         **meta)[0]
1279
                                         meta)[0]
1228 1280
        
1229 1281
        #assert successful move
1230 1282
        self.assertEqual(status, 201)
b/pithos/lib/client.py
365 365
        return self.post(path, data, headers=headers)
366 366
    
367 367
    def _change_obj_location(self, src_container, src_object, dst_container,
368
                             dst_object, remove=False, public=False, **meta):
368
                             dst_object, remove=False, meta={}, **headers):
369 369
        path = '/%s/%s' % (dst_container, dst_object)
370
        headers = {}
370
        headers = {} if not headers else headers
371 371
        for k, v in meta.items():
372 372
            headers['x-object-meta-%s' % k] = v 
373 373
        if remove:
374 374
            headers['x-move-from'] = '/%s/%s' % (src_container, src_object)
375 375
        else:
376 376
            headers['x-copy-from'] = '/%s/%s' % (src_container, src_object)
377
        self._set_public_header(headers, public)
378
        self.headers = headers if headers else None
379
        headers['content-length'] = 0
377
        headers['content_length'] = 0
380 378
        return self.put(path, headers=headers)
381 379
    
382
    def copy_object(self, src_container, src_object, dst_container,
383
                             dst_object, public=False, **meta):
380
    def copy_object(self, src_container, src_object, dst_container, dst_object,
381
                    meta, **headers):
382
        """copies an object"""
384 383
        return self._change_obj_location(src_container, src_object,
385
                                   dst_container, dst_object, False,
386
                                   public, **meta)
384
                                   dst_container, dst_object, remove=False,
385
                                   meta=meta, **headers)
387 386
    
388 387
    def move_object(self, src_container, src_object, dst_container,
389
                             dst_object, public=False, **meta):
388
                             dst_object, meta={}, **headers):
389
        """moves an object"""
390 390
        return self._change_obj_location(src_container, src_object,
391
                                   dst_container, dst_object, True,
392
                                   public, **meta)
391
                                         dst_container, dst_object, remove=True,
392
                                         meta=meta, **headers)
393 393
    
394 394
    def delete_object(self, container, object, params={}):
395
        """deletes an object"""
395 396
        return self.delete('/%s/%s' % (container, object), params=params)
396 397
    
397 398
    def retrieve_object_metadata(self, container, object, restricted=False,
......
757 758
        """restores a trashed object"""
758 759
        return self.delete_object_metadata(container, object, ['trash'])
759 760
    
760
    def _set_public_header(self, headers, public=False):
761
        """sets the public header"""
762
        if not headers:
763
            headers = {}
764
        if public == None:
765
            return
766
        else:
767
            headers['x-object-public'] = public if public else ''
768
    
769 761
    def publish_object(self, container, object):
770 762
        """sets a previously created object publicly accessible"""
771 763
        path = '/%s/%s' % (container, object)
772 764
        headers = {'content_range':'bytes */*'}
773
        self._set_public_header(headers, public=True)
765
        headers['x_object_public'] = True
774 766
        return self.post(path, headers=headers)
775 767
    
776 768
    def unpublish_object(self, container, object):
777 769
        """unpublish an object"""
778 770
        path = '/%s/%s' % (container, object)
779 771
        headers = {'content_range':'bytes */*'}
780
        self._set_public_header(headers, public=False)
772
        headers['x_object_public'] = False
781 773
        return self.post(path, headers=headers)
774
    
775
    def _change_obj_location(self, src_container, src_object, dst_container,
776
                             dst_object, remove=False, meta={}, **headers):
777
        path = '/%s/%s' % (dst_container, dst_object)
778
        headers = {} if not headers else headers
779
        for k, v in meta.items():
780
            headers['x-object-meta-%s' % k] = v 
781
        if remove:
782
            headers['x-move-from'] = '/%s/%s' % (src_container, src_object)
783
        else:
784
            headers['x-copy-from'] = '/%s/%s' % (src_container, src_object)
785
        headers['content_length'] = 0
786
        return self.put(path, headers=headers)
787
    
788
    def copy_object(self, src_container, src_object, dst_container, dst_object,
789
                    meta={}, public=False, version=None):
790
        """copies an object"""
791
        headers = {}
792
        headers['x_object_public'] = public
793
        if version:
794
            headers['x_object_version'] = version
795
        return OOS_Client.copy_object(self, src_container, src_object,
796
                                      dst_container, dst_object, meta=meta,
797
                                      **headers)
798
    
799
    def move_object(self, src_container, src_object, dst_container,
800
                             dst_object, meta={}, public=False, version=None):
801
        """moves an object"""
802
        headers = {}
803
        headers['x_object_public'] = public
804
        if version:
805
            headers['x_object_version'] = version
806
        return OOS_Client.move_object(self, src_container, src_object,
807
                                      dst_container, dst_object, meta=meta,
808
                                      **headers)
b/tools/store
210 210
        if getattr(self, 'until'):
211 211
            t = _time.strptime(self.until, self.format)
212 212
            args['until'] = int(_time.mktime(t))
213
            
213
        
214 214
        if object:
215 215
            meta = self.client.retrieve_object_metadata(container, object,
216 216
                                                        self.restricted,
......
372 372
                          help='file descriptor to read from (pass - for standard input)')
373 373
        parser.add_option('--public', action='store_true',
374 374
                          dest='x_object_public', default=False,
375
                          help='make object publicly accessible (\'True\'/\'False\')')
375
                          help='make object publicly accessible')
376 376
    
377 377
    def execute(self, path, *args):
378 378
        if path.find('=') != -1:
......
416 416

  
417 417
@cli_command('copy', 'cp')
418 418
class CopyObject(Command):
419
    syntax = '<src container>/<src object> [<dst container>/]<dst object>'
419
    syntax = '<src container>/<src object> [<dst container>/]<dst object> [key=val] [...]'
420 420
    description = 'copy an object to a different location'
421 421
    
422 422
    def add_options(self, parser):
423 423
        parser.add_option('--version', action='store',
424 424
                          dest='version', default=False,
425 425
                          help='copy specific version')
426
        parser.add_option('--public', action='store',
427
                          dest='public', default=None,
428
                          help='publish/unpublish object (\'True\'/\'False\')')
426
        parser.add_option('--public', action='store_true',
427
                          dest='public', default=False,
428
                          help='make object publicly accessible')
429 429
    
430
    def execute(self, src, dst):
430
    def execute(self, src, dst, *args):
431 431
        src_container, sep, src_object = src.partition('/')
432 432
        dst_container, sep, dst_object = dst.partition('/')
433
        
434
        #prepare user defined meta
435
        meta = {}
436
        for arg in args:
437
            key, sep, val = arg.partition('=')
438
            meta[key] = val
439
        
433 440
        if not sep:
434 441
            dst_container = src_container
435 442
            dst_object = dst
436
        version = getattr(self, 'version')
437
        headers = None
438
        if version:
439
            headers = {}
440
            headers['X_SOURCE_VERSION'] = version
441
        if self.public and self.nopublic:
442
            raise Fault('Conflicting options')
443
        if self.public not in ['True', 'False', None]:
444
            raise Fault('Not acceptable value for public')
445
        public = eval(self.public) if self.public else None
443
        
446 444
        self.client.copy_object(src_container, src_object, dst_container,
447
                                dst_object, public, headers)
445
                                dst_object, meta, self.public, self.version, **meta)
448 446

  
449 447
@cli_command('set')
450 448
class SetMeta(Command):
......
503 501
        parser.add_option('-f', action='store',
504 502
                          dest='srcpath', default=None,
505 503
                          help='file descriptor to read from: pass - for standard input')
506
        parser.add_option('--public', action='store',
504
        parser.add_option('--public', action='store_true',
507 505
                          dest='x_object_public', default=False,
508
                          help='publish/unpublish object (\'True\'/\'False\')')
506
                          help='make object publicly accessible')
509 507
    
510 508
    def execute(self, path, *args):
511 509
        if path.find('=') != -1:
......
545 543
    description = 'move an object to a different location'
546 544
    
547 545
    def add_options(self, parser):
548
        parser.add_option('--public', action='store',
549
                          dest='public', default=None,
550
                          help='publish/unpublish object (\'True\'/\'False\')')
546
        parser.add_option('--version', action='store',
547
                          dest='version', default=None,
548
                          help='move a specific object version')
549
        parser.add_option('--public', action='store_true',
550
                          dest='public', default=False,
551
                          help='make object publicly accessible')
551 552
    
552
    def execute(self, src, dst):
553
    def execute(self, src, dst, *args):
553 554
        src_container, sep, src_object = src.partition('/')
554 555
        dst_container, sep, dst_object = dst.partition('/')
555 556
        if not sep:
556 557
            dst_container = src_container
557 558
            dst_object = dst
558
        if self.public not in ['True', 'False', None]:
559
            raise Fault('Not acceptable value for public')
560
        public = eval(self.public) if self.public else None
559
        
560
        #prepare user defined meta
561
        meta = {}
562
        for arg in args:
563
            key, sep, val = arg.partition('=')
564
            meta[key] = val
565
        
561 566
        self.client.move_object(src_container, src_object, dst_container,
562
                                dst_object, public, headers)
567
                                dst_object, meta, self.public, self.version)
563 568

  
564 569
@cli_command('remove')
565 570
class TrashObject(Command):
......
741 746
    
742 747
    cmd = cls(name, argv[2:])
743 748
    
744
    #cmd.execute(*cmd.args)
745
    try:
746
        cmd.execute(*cmd.args)
747
    except TypeError, e:
748
        cmd.parser.print_help()
749
        exit(1)
750
    except Fault, f:
751
        status = f.status and '%s ' % f.status or ''
752
        print '%s%s' % (status, f.data)
749
    cmd.execute(*cmd.args)
750
    #try:
751
    #    cmd.execute(*cmd.args)
752
    #except TypeError, e:
753
    #    cmd.parser.print_help()
754
    #    exit(1)
755
    #except Fault, f:
756
    #    status = f.status and '%s ' % f.status or ''
757
    #    print '%s%s' % (status, f.data)
753 758

  
754 759
if __name__ == '__main__':
755 760
    main()

Also available in: Unified diff