Revision ae75584f

b/pithos/api/tests.py
1
#coding=utf8
2

  
1 3
# Copyright 2011 GRNET S.A. All rights reserved.
2 4
# 
3 5
# Redistribution and use in source and binary forms, with or
......
56 58
DEFAULT_USER = 'test'
57 59
DEFAULT_AUTH = '0000'
58 60

  
61
OTHER_ACCOUNTS = {
62
    '0001': 'verigak',
63
    '0002': 'chazapis',
64
    '0003': 'gtsouk',
65
    '0004': 'papagian',
66
    '0005': 'louridas',
67
    '0006': 'chstath',
68
    '0007': 'pkanavos',
69
    '0008': 'mvasilak'}
70

  
59 71
class BaseTestCase(unittest.TestCase):
60 72
    #TODO unauthorized request
61 73
    def setUp(self):
......
110 122
                'name',
111 123
                'count',
112 124
                'bytes',
113
                'last_modified'),
125
                'last_modified',
126
                'x_container_policy_quota',
127
                'x_container_policy_versioning',),
114 128
            'object':(
115 129
                'name',
116 130
                'hash',
......
122 136
    
123 137
    def tearDown(self):
124 138
        for c in self.client.list_containers():
125
            for o in self.client.list_objects(c):
126
                self.client.delete_object(c, o)
139
            while True:
140
                #list objects returns at most 10000 objects
141
                #so repeat until there are no more objects
142
                objects = self.client.list_objects(c)
143
                if not objects:
144
                    break
145
                for o in objects:
146
                    self.client.delete_object(c, o)
127 147
            self.client.delete_container(c)
128 148
    
129 149
    def assert_status(self, status, codes):
......
134 154
            l.append(codes)
135 155
        self.assertTrue(status in l)
136 156
    
137
    #def assert_list(self, path, entity, limit=10000, format='text', params=None, **headers):
138
    #    status, headers, data = self.client.get(path, format=format,
139
    #                                            headers=headers, params=params)
140
    #    
141
    #    self.assert_status(status, [200, 204, 304, 412])
142
    #    if format == 'text':
143
    #        data = data.strip().split('\n') if data else []
144
    #        self.assertTrue(len(data) <= limit)
145
    #    else:
146
    #        exp_content_type = self.contentTypes[format]
147
    #        self.assertEqual(headers['content_type'].find(exp_content_type), 0)
148
    #        #self.assert_extended(data, format, entity, limit)
149
    #        if format == 'json':
150
    #            data = json.loads(data) if data else []
151
    #        elif format == 'xml':
152
    #            data = minidom.parseString(data)
153
    #    return status, headers, data
154
    
155 157
    #def assert_headers(self, headers, type, **exp_meta):
156 158
    #    prefix = 'x-%s-meta-' %type
157 159
    #    system_headers = [h for h in headers if not h.startswith(prefix)]
......
162 164
    #            k = k.split(prefix)[-1]
163 165
    #            self.assertEqual(v, exp_meta[k])
164 166
    
165
    def assert_extended(self, data, format, type, size):
167
    def assert_extended(self, data, format, type, size=10000):
166 168
        if format == 'xml':
167 169
            self._assert_xml(data, type, size)
168 170
        elif format == 'json':
......
190 192
        self.assertTrue(len(entities) <= size)
191 193
        for e in entities:
192 194
            for item in info:
193
                self.assertTrue(e.hasAttribute(item))
195
                self.assertTrue(e.getElementsByTagName(item))
194 196
    
195 197
    def assert_raises_fault(self, status, callableObj, *args, **kwargs):
196 198
        """
......
198 200
        when callableObj is called with the specific arguments
199 201
        """
200 202
        try:
201
            callableObj(*args, **kwargs)
203
            r = callableObj(*args, **kwargs)
202 204
            self.fail('Should never reach here')
203 205
        except Fault, f:
204 206
            self.failUnless(f.status == status)
......
244 246
            args = {}
245 247
            args['etag'] = etag if etag else obj['hash']
246 248
            
247
            guess = mimetypes.guess_type(name)
248
            type = type if type else guess[0]
249
            enc = enc if enc else guess[1]
249
            try:
250
                guess = mimetypes.guess_type(name)
251
                type = type if type else guess[0]
252
                enc = enc if enc else guess[1]
253
            except:
254
                pass
250 255
            args['content_type'] = type if type else 'plain/text'
251 256
            args['content_encoding'] = enc if enc else None
252 257
            
......
259 264
            return obj
260 265
        except IOError:
261 266
            return
267
class TopLevel(BaseTestCase):
268
    def test_list_shared_by_other(self):
269
        pass
262 270

  
263 271
class AccountHead(BaseTestCase):
264 272
    def setUp(self):
......
333 341
        meta = self.client.retrieve_account_metadata(restricted=True,
334 342
                                                     until='kshfksfh')
335 343
        self.assertTrue('premium' in meta)
336

  
344
    
337 345
class AccountGet(BaseTestCase):
338 346
    def setUp(self):
339 347
        BaseTestCase.setUp(self)
......
381 389
        l = 2
382 390
        m = 'oranges'
383 391
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
384
        #self.assert_extended(xml, 'xml', 'container', l)
392
        self.assert_extended(xml, 'xml', 'container', l)
385 393
        nodes = xml.getElementsByTagName('name')
386 394
        self.assertEqual(len(nodes), 1)
387 395
        self.assertEqual(nodes[0].childNodes[0].data, 'pears')
......
591 599
        l.sort()
592 600
        self.assertEqual(objects, l)
593 601
    
602
    def test_list_objects_containing_slash(self):
603
        self.client.create_container('test')
604
        self.upload_random_data('test', '/objectname')
605
        
606
        objects = self.client.list_objects('test')
607
        self.assertEqual(objects, ['/objectname'])
608
        
609
        objects = self.client.list_objects('test', format='json')
610
        self.assertEqual(objects[0]['name'], '/objectname')
611
        
612
        objects = self.client.list_objects('test', format='xml')
613
        self.assert_extended(objects, 'xml', 'object')
614
        node_name = objects.getElementsByTagName('name')[0]
615
        self.assertEqual(node_name.firstChild.data, '/objectname')
616
        
617
        #objects = self.client.list_objects('test', prefix='/')
618
        #self.assertEqual(objects, ['/objectname'])
619
        #
620
        #objects = self.client.list_objects('test', path='/')
621
        #self.assertEqual(objects, ['/objectname'])
622
        #
623
        #objects = self.client.list_objects('test', prefix='/', delimiter='n')
624
        #self.assertEqual(objects, ['/object'])
625

  
594 626
    def test_list_objects_with_limit_marker(self):
595 627
        objects = self.client.list_objects(self.container[0], limit=2)
596 628
        l = [elem['name'] for elem in self.obj[:8]]
......
610 642
            end = len(l) >= end and end or len(l)
611 643
            self.assertEqual(objects, l[start:end])
612 644
    
645
    #takes too long
646
    #def test_list_limit_exceeds(self):
647
    #    self.client.create_container('pithos')
648
    #    
649
    #    for i in range(10001):
650
    #        self.client.create_zero_length_object('pithos', i)
651
    #    
652
    #    self.assertEqual(10000, len(self.client.list_objects('pithos')))
653
    
654
    def test_list_empty_params(self):
655
        objects = self.client.get('/%s' % self.container[0])[2]
656
        if objects:
657
            objects = objects.strip().split('\n')
658
        self.assertEqual(objects,
659
                         self.client.list_objects(self.container[0]))
660
    
613 661
    def test_list_pseudo_hierarchical_folders(self):
614 662
        objects = self.client.list_objects(self.container[1], prefix='photos',
615 663
                                           delimiter='/')
......
635 683
    def test_extended_list_xml(self):
636 684
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
637 685
                                       prefix='photos', delimiter='/')
686
        self.assert_extended(xml, 'xml', 'object', size=4)
638 687
        dirs = xml.getElementsByTagName('subdir')
639 688
        self.assertEqual(len(dirs), 2)
640 689
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
......
1160 1209
        #assert content-type
1161 1210
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1162 1211
    
1212
    def test_maximum_upload_size_exceeds(self):
1213
        name = o_names[0]
1214
        meta = {'test':'test1'}
1215
        #upload 100MB
1216
        length=1024*1024*100
1217
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1218
                                 name, length, **meta)
1219
        
1220
        ##d = get_random_data(length=1024*1024*100)
1221
        ##self.client.create_object_using_chunks(self.container, name, StringIO(d))
1222
        
1223

  
1163 1224
    def test_upload_with_name_containing_slash(self):
1164 1225
        name = '/%s' % o_names[0]
1165 1226
        meta = {'test':'test1'}
......
1191 1252
        
1192 1253
        uploaded_data = self.client.retrieve_object(self.container, objname)
1193 1254
        self.assertEqual(data, uploaded_data)
1194

  
1255
    
1256
    def test_manifestation(self):
1257
        prefix = 'myobject/'
1258
        data = ''
1259
        for i in range(5):
1260
            part = '%s%d' %(prefix, i)
1261
            o = self.upload_random_data(self.container, part)
1262
            data += o['data']
1263
        
1264
        manifest = '%s/%s' %(self.container, prefix)
1265
        self.client.create_manifestation(self.container, 'large-object',
1266
                                         manifest)
1267
        
1268
        self.assert_object_exists(self.container, 'large-object')
1269
        self.assertEqual(data, self.client.retrieve_object(self.container,
1270
                                                           'large-object'))
1271
        
1272
        #wrong manifestation
1273
        self.client.create_manifestation(self.container, 'large-object',
1274
                                         'invalid')
1275
        
1195 1276
class ObjectCopy(BaseTestCase):
1196 1277
    def setUp(self):
1197 1278
        BaseTestCase.setUp(self)
......
1201 1282
            self.client.create_container(c)
1202 1283
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1203 1284
    
1285
    def tearDown(self):
1286
        pass
1287
    
1204 1288
    def test_copy(self):
1205 1289
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1206 1290
                             self.containers[0], self.obj['name']):
......
1259 1343
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1260 1344
                                 self.obj['name'], self.containers[1],
1261 1345
                                 'testcopy', meta)
1262
        
1263 1346

  
1264 1347
class ObjectMove(BaseTestCase):
1265 1348
    def setUp(self):
......
1426 1509
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1427 1510
                                 self.obj['name'])
1428 1511

  
1512
class ListSharing(BaseTestCase):
1513
    def setUp(self):
1514
        BaseTestCase.setUp(self)
1515
        self.client.create_container('c')
1516
        for i in range(2):
1517
            self.upload_random_data('c', 'o%s' %i)
1518
        accounts = OTHER_ACCOUNTS.copy()
1519
        self.o1_sharing_with = accounts.popitem()
1520
        self.o1_sharing = [self.o1_sharing_with[1]]
1521
        self.client.share_object('c', 'o1', self.o1_sharing, read=True)
1522
        
1523
        l = []
1524
        for i in range(2):
1525
            l.append(accounts.popitem())
1526
        #self.client.set_account_groups({'pithos-dev':'chazapis,verigak,papagian'})
1527
        #self.o2_sharing = 'write=%s' % 
1528
        #self.client.share_object('c', 'o2', self.o2_sharing)
1529
    
1530
    def test_listing(self):
1531
        self.other = Pithos_Client(DEFAULT_HOST,
1532
                              self.o1_sharing_with[0],
1533
                              self.o1_sharing_with[1],
1534
                              DEFAULT_API)
1535
        self.assertTrue('test' in self.other.list_shared_by_others())
1536

  
1537
class TestGreek(BaseTestCase):
1538
    def tearDown(self):
1539
        pass
1540
    
1541
    def test_create_container(self):
1542
        self.client.create_container('φάκελος')
1543
        self.assert_container_exists('φάκελος')
1544
    
1545
    def test_create_object(self):
1546
        self.client.create_container('φάκελος')
1547
        self.upload_random_data('φάκελος', 'αντικείμενο')
1548
        
1549
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1550
    
1551
    def test_copy_object(self):
1552
        self.client.create_container('φάκελος')
1553
        self.upload_random_data('φάκελος', 'αντικείμενο')
1554
        
1555
        self.client.create_container('αντίγραφα')
1556
        self.client.copy_object('φάκελος', 'αντικείμενο', 'αντίγραφα',
1557
                                'αντικείμενο')
1558
        
1559
        self.assert_object_exists('αντίγραφα', 'αντικείμενο')
1560
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1561
    
1562
    def test_move_object(self):
1563
        self.client.create_container('φάκελος')
1564
        self.upload_random_data('φάκελος', 'αντικείμενο')
1565
        
1566
        self.client.create_container('αντίγραφα')
1567
        self.client.copy_object('φάκελος', 'αντικείμενο', 'αντίγραφα',
1568
                                'αντικείμενο')
1569
        
1570
        self.assert_object_exists('αντίγραφα', 'αντικείμενο')
1571
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1572
    
1573
    def test_delete_object(self):
1574
        pass
1575
    
1576
    def test_delete_container(self):
1577
        pass
1578
    
1579
    def test_account_meta(self):
1580
        pass
1581
    
1582
    def test_container_meta(self):
1583
        pass
1584
    
1585
    def test_obejct_meta(self):
1586
        pass
1587
    
1588
    def test_list_meta_filtering(self):
1589
        pass
1590
    
1591
    def test_groups(self):
1592
        pass
1593
    
1594
    def test_permissions(self):
1595
        pass
1596
    
1429 1597
class AssertMappingInvariant(object):
1430 1598
    def __init__(self, callable, *args, **kwargs):
1431 1599
        self.callable = callable
b/pithos/lib/client.py
38 38
import json
39 39
import types
40 40
import socket
41
import urllib
41 42
import pithos.api.faults
42 43

  
43 44
ERROR_CODES = {304:'Not Modified',
......
71 72
        self.token = token
72 73
    
73 74
    def _req(self, method, path, body=None, headers={}, format='text',
74
            params={}):
75
        full_path = '/%s/%s%s?format=%s' % (self.api, self.account, path,
76
                                            format)
75
             params={}, top_level_req=False):
76
        path = urllib.quote(path)
77
        account = '' if top_level_req else self.account
78
        full_path = '/%s/%s%s?format=%s' % (self.api, account, path, format)
79
        
77 80
        for k,v in params.items():
78 81
            if v:
79 82
                full_path = '%s&%s=%s' %(full_path, k, v)
......
97 100
            kwargs['headers'].setdefault('content-type',
98 101
                                         'application/octet-stream')
99 102
        kwargs['headers'].setdefault('content-length', len(body) if body else 0)
100
        try:
101
            #print '*', method, full_path, kwargs
102
            conn.request(method, full_path, **kwargs)
103
        except socket.error, e:
104
            raise Fault(status=503)
103
        kwargs['headers'] = _encode_headers(kwargs['headers'])
104
        #print '#', method, full_path, kwargs
105
        conn.request(method, full_path, **kwargs)
106
        
107
        #try:
108
        #    conn.request(method, full_path, **kwargs)
109
        #except socket.error, e:
110
        #    print '###', e[0], conn.auto_open
111
        #    raise Fault(status=503)
105 112
            
106 113
        resp = conn.getresponse()
107 114
        headers = dict(resp.getheaders())
......
128 135
    def delete(self, path, format='text', params={}):
129 136
        return self._req('DELETE', path, format=format, params=params)
130 137
    
131
    def get(self, path, format='text', headers=None, params={}):
138
    def get(self, path, format='text', headers={}, params={},
139
            top_level_req=False):
132 140
        return self._req('GET', path, headers=headers, format=format,
133
                        params=params)
141
                        params=params, top_level_req=top_level_req)
134 142
    
135 143
    def head(self, path, format='text', params={}):
136
        return self._req('HEAD', path, format=format, params=params)
144
         return self._req('HEAD', path, format=format, params=params)
137 145
    
138 146
    def post(self, path, body=None, format='text', headers=None, params={}):
139 147
        return self._req('POST', path, body, headers=headers, format=format,
......
142 150
    def put(self, path, body=None, format='text', headers=None):
143 151
        return self._req('PUT', path, body, headers=headers, format=format)
144 152
    
145
    def _list(self, path, format='text', params={}, **headers):
153
    def _list(self, path, format='text', params={}, top_level_req=False, 
154
              **headers):
146 155
        status, headers, data = self.get(path, format=format, headers=headers,
147
                                         params=params)
156
                                         params=params,
157
                                         top_level_req=top_level_req)
148 158
        if format == 'json':
149 159
            data = json.loads(data) if data else ''
150 160
        elif format == 'xml':
......
216 226
    
217 227
    # Storage Account Services
218 228
    
219
    def list_containers(self, format='text', limit=10000, marker=None, params={},
229
    def list_containers(self, format='text', limit=None, marker=None, params={},
220 230
                        **headers):
221 231
        """lists containers"""
222
        if not params:
223
            params = {}
224 232
        params.update({'limit':limit, 'marker':marker})
225 233
        return self._list('', format, params, **headers)
226 234
    
......
246 254
    def _filter_trashed(self, l):
247 255
        return self._filter(l, {'trash':'true'})
248 256
    
249
    def list_objects(self, container, format='text', limit=10000, marker=None,
257
    def list_objects(self, container, format='text', limit=None, marker=None,
250 258
                     prefix=None, delimiter=None, path=None,
251 259
                     include_trashed=False, params={}, **headers):
252 260
        """returns a list with the container objects"""
......
369 377
        path = '/%s/%s' % (dst_container, dst_object)
370 378
        headers = {} if not headers else headers
371 379
        for k, v in meta.items():
372
            headers['x-object-meta-%s' % k] = v 
380
            headers['x-object-meta-%s' % k] = v
373 381
        if remove:
374 382
            headers['x-move-from'] = '/%s/%s' % (src_container, src_object)
375 383
        else:
......
440 448
            block = f.read(blocksize)
441 449
            if block == '':
442 450
                break
443
            data = '%s\r\n%s\r\n' % (hex(len(block)), block)
451
            data = '%x\r\n%s\r\n' % (len(block), block)
444 452
            try:
445 453
                http.send(data)
446 454
            except:
447 455
                #retry
448 456
                http.send(data)
449
        data = '0x0\r\n'
457
        data = '0\r\n\r\n'
450 458
        try:
451 459
            http.send(data)
452 460
        except:
......
505 513
                        if_unmodified_since=None, limit=1000, marker=None,
506 514
                        until=None):
507 515
        """returns a list with the account containers"""
508
        params = {'until':until} if until else None
516
        params = {'until':until} if until else {}
509 517
        headers = {'if-modified-since':if_modified_since,
510 518
                   'if-unmodified-since':if_unmodified_since}
511 519
        return OOS_Client.list_containers(self, format=format, limit=limit,
......
521 529
    def set_account_groups(self, **groups):
522 530
        """create account groups"""
523 531
        headers = {}
524
        for key, val in groups.items():
525
            headers['x-account-group-%s' % key] = val
532
        for k, v in groups.items():
533
            headers['x-account-group-%s' % k] = v
526 534
        params = {'update':None}
527 535
        return self.post('', headers=headers, params=params)
528 536
    
......
551 559
    def reset_account_groups(self, **groups):
552 560
        """overrides account groups"""
553 561
        headers = {}
554
        for key, val in groups.items():
555
            headers['x-account-group-%s' % key] = val
562
        for k, v in groups.items():
563
            v = v.strip()
564
            headers['x-account-group-%s' % k] = v
556 565
        meta = self.retrieve_account_metadata()
557 566
        headers.update(meta)
558 567
        return self.post('', headers=headers)
559 568
    
560 569
    # Storage Container Services
561 570
    
562
    def list_objects(self, container, format='text', limit=10000, marker=None,
571
    def list_objects(self, container, format='text', limit=None, marker=None,
563 572
                     prefix=None, delimiter=None, path=None,
564 573
                     include_trashed=False, params={}, if_modified_since=None,
565 574
                     if_unmodified_since=None, meta={}, until=None):
......
672 681
            headers.update({elem:eval(elem)})
673 682
        
674 683
        for k,v in meta.items():
675
            headers['x-object-meta-%s' %k.strip()] = v.strip()
684
            v = v.strip()
685
            headers['x-object-meta-%s' %k.strip()] = v
676 686
        
677 687
        return self._chunked_transfer(path, 'PUT', f, headers=headers,
678 688
                                      blocksize=blocksize)
......
738 748
            headers['content_range'] = 'bytes */*'
739 749
        
740 750
        for k,v in meta.items():
741
            headers['x-object-meta-%s' %k.strip()] = v.strip()
751
            v = v.strip()
752
            headers['x-object-meta-%s' %k.strip()] = v
742 753
        
743 754
        return self._chunked_transfer(path, 'POST', f, headers=headers,
744 755
                                      blocksize=blocksize)
......
777 788
        path = '/%s/%s' % (dst_container, dst_object)
778 789
        headers = {} if not headers else headers
779 790
        for k, v in meta.items():
780
            headers['x-object-meta-%s' % k] = v 
791
            headers['x-object-meta-%s' % k] = v
781 792
        if remove:
782 793
            headers['x-move-from'] = '/%s/%s' % (src_container, src_object)
783 794
        else:
......
805 816
            headers['x_object_version'] = version
806 817
        return OOS_Client.move_object(self, src_container, src_object,
807 818
                                      dst_container, dst_object, meta=meta,
808
                                      **headers)
819
                                      **headers)
820
    
821
    def list_shared_by_others(self, limit=None, marker=None, format='text'):
822
         l = ['limit', 'marker']
823
         params = {}
824
         for elem in [elem for elem in l if eval(elem)]:
825
             params[elem] = eval(elem)
826
         return self._list('', format, params, top_level_req=True)
827
    
828
    def share_object(self, container, object, l, read=True):
829
        action = 'read' if read else 'write'
830
        sharing = '%s=%s' % (action, ','.join(l))
831
        self.update_object(container, object, f=None, x_object_sharing=sharing)
832

  
833
def _encode_headers(headers):
834
    h = {}
835
    for k, v in headers.items():
836
        k = urllib.quote(k)
837
        if v and type(v) == types.StringType:
838
            v = urllib.quote(v, '/=,-* :"')
839
        h[k] = v
840
    return h
b/tools/store
47 47
import time as _time
48 48
import os
49 49

  
50
#DEFAULT_HOST = 'pithos.dev.grnet.gr'
51
DEFAULT_HOST = '127.0.0.1:8000'
50
DEFAULT_HOST = 'pithos.dev.grnet.gr'
51
#DEFAULT_HOST = '127.0.0.1:8000'
52 52
DEFAULT_API = 'v1'
53 53

  
54 54
_cli_commands = {}
......
685 685
    print '\nCommands:\n' + '\n'.join(sorted(commands))
686 686

  
687 687
def print_dict(d, header='name', f=stdout, detail=True):
688
    header = header in d and header or 'subdir'
688
    header = header if header in d else 'subdir'
689 689
    if header and header in d:
690
        f.write('%s\n' %d.pop(header))
690
        f.write('%s\n' %d.pop(header).encode('utf8'))
691 691
    if detail:
692 692
        patterns = ['^x_(account|container|object)_meta_(\w+)$']
693 693
        patterns.append(patterns[0].replace('_', '-'))

Also available in: Unified diff