Revision e7b51248

b/pithos/api/functions.py
39 39
from django.template.loader import render_to_string
40 40
from django.utils import simplejson as json
41 41
from django.utils.http import parse_etags
42
from django.utils.encoding import smart_unicode, smart_str
42 43
from xml.dom import minidom
43 44

  
44 45
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound, Conflict,
......
439 440
    keys = request.GET.get('meta')
440 441
    if keys:
441 442
        keys = keys.split(',')
442
        keys = [format_header_key('X-Object-Meta-' + x.strip()) for x in keys if x.strip() != '']
443
        l = [smart_str(x) for x in keys if x.strip() != '']
444
        keys = [format_header_key('X-Object-Meta-' + x.strip()) for x in l]
443 445
    else:
444 446
        keys = []
445 447
    
......
677 679
            meta = {}
678 680
        validate_matching_preconditions(request, meta)
679 681
    
680
    copy_from = request.META.get('HTTP_X_COPY_FROM')
681
    move_from = request.META.get('HTTP_X_MOVE_FROM')
682
    copy_from = smart_unicode(request.META.get('HTTP_X_COPY_FROM'), strings_only=True)
683
    move_from = smart_unicode(request.META.get('HTTP_X_MOVE_FROM'), strings_only=True)
682 684
    if copy_from or move_from:
683 685
        content_length = get_content_length(request) # Required by the API.
684 686
        
b/pithos/api/tests.py
34 34
# or implied, of GRNET S.A.
35 35

  
36 36
from pithos.lib.client import Pithos_Client, Fault
37
from django.utils import simplejson as json
38 37
from xml.dom import minidom
39 38
from StringIO import StringIO
39
import json
40 40
import unittest
41 41
import time as _time
42 42
import types
......
66 66
    '0005': 'louridas',
67 67
    '0006': 'chstath',
68 68
    '0007': 'pkanavos',
69
    '0008': 'mvasilak'}
69
    '0008': 'mvasilak',
70
    '0009': 'κούκης'}
70 71

  
71 72
class BaseTestCase(unittest.TestCase):
72 73
    #TODO unauthorized request
......
205 206
        except Fault, f:
206 207
            self.failUnless(f.status == status)
207 208
    
209
    def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
210
        """
211
        asserts that a Fault with a specific status is not raised
212
        when callableObj is called with the specific arguments
213
        """
214
        try:
215
            r = callableObj(*args, **kwargs)
216
        except Fault, f:
217
            self.failIf(f.status == status)
218
    
208 219
    def assert_container_exists(self, container):
209 220
        """
210 221
        asserts the existence of a container
......
214 225
        except Fault, f:
215 226
            self.failIf(f.status == 404)
216 227
    
228
    def assert_container_not_exists(self, container):
229
        """
230
        asserts there is no such a container
231
        """
232
        self.assert_raises_fault(404, self.client.retrieve_container_metadata,
233
                                 container)
234
    
217 235
    def assert_object_exists(self, container, object):
218 236
        """
219 237
        asserts the existence of an object
......
264 282
            return obj
265 283
        except IOError:
266 284
            return
267
class TopLevel(BaseTestCase):
268
    def test_list_shared_by_other(self):
269
        pass
270 285

  
271 286
class AccountHead(BaseTestCase):
272 287
    def setUp(self):
......
301 316
                l.append(g)
302 317
        self.client.unset_account_groups(l)
303 318
        
304
        #print '#', self.client.retrieve_account_groups()
305
        #print '#', self.client.retrieve_account_metadata(restricted=True)
306 319
        BaseTestCase.tearDown(self)
307 320
    
308 321
    def test_get_account_meta(self):
......
481 494
                l.append(g)
482 495
        self.client.unset_account_groups(l)
483 496
        
484
        #print '#', self.client.retrieve_account_groups()
485
        #print '#', self.client.retrieve_account_metadata(restricted=True)
486 497
        BaseTestCase.tearDown(self)
487 498
    
488 499
    def test_update_meta(self):
......
533 544
            self.client.set_account_groups(**more_groups)
534 545
            
535 546
            groups.update(more_groups)
536
            self.assertEqual(groups, self.client.retrieve_account_groups())
547
            self.assertEqual(set(groups['pithosdev']),
548
                             set(self.client.retrieve_account_groups()['pithosdev']))
537 549
    
538 550
    def test_reset_account_groups(self):
539 551
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
......
543 555
            
544 556
            self.assertEqual(groups, self.client.retrieve_account_groups())
545 557
            
546
            groups = {'pithosdev':'verigak,gtsouk,chazapis, papagian'}
558
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
547 559
            self.client.reset_account_groups(**groups)
548 560
            
549
            self.assertTrue(groups, self.client.retrieve_account_groups())
561
            print '#', groups, self.client.retrieve_account_groups()
562
            self.assertEqual(groups['pithosdev'].split(','),
563
                             self.client.retrieve_account_groups()['pithosdev'].split(','))
550 564
    
551 565
    def test_delete_account_groups(self):
552 566
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
......
643 657
            self.assertEqual(objects, l[start:end])
644 658
    
645 659
    #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')))
660
    def _test_list_limit_exceeds(self):
661
        self.client.create_container('pithos')
662
        
663
        for i in range(10001):
664
            self.client.create_zero_length_object('pithos', i)
665
        
666
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
653 667
    
654 668
    def test_list_empty_params(self):
655 669
        objects = self.client.get('/%s' % self.container[0])[2]
......
1209 1223
        #assert content-type
1210 1224
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1211 1225
    
1212
    def test_maximum_upload_size_exceeds(self):
1226
    def _test_maximum_upload_size_exceeds(self):
1213 1227
        name = o_names[0]
1214 1228
        meta = {'test':'test1'}
1215 1229
        #upload 100MB
1216 1230
        length=1024*1024*100
1217 1231
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1218 1232
                                 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

  
1233
    
1224 1234
    def test_upload_with_name_containing_slash(self):
1225 1235
        name = '/%s' % o_names[0]
1226 1236
        meta = {'test':'test1'}
......
1535 1545
        self.assertTrue('test' in self.other.list_shared_by_others())
1536 1546

  
1537 1547
class TestGreek(BaseTestCase):
1548
    def setUp(self):
1549
        BaseTestCase.setUp(self)
1550
        #keep track of initial account groups
1551
        self.initial_groups = self.client.retrieve_account_groups()
1552
        
1553
        #keep track of initial account meta
1554
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1555
    
1538 1556
    def tearDown(self):
1539
        pass
1557
        #delete additionally created meta
1558
        l = []
1559
        for m in self.client.retrieve_account_metadata(restricted=True):
1560
            if m not in self.initial_meta:
1561
                l.append(m)
1562
        self.client.delete_account_metadata(l)
1563
        
1564
        #delete additionally created groups
1565
        l = []
1566
        for g in self.client.retrieve_account_groups():
1567
            if g not in self.initial_groups:
1568
                l.append(g)
1569
        self.client.unset_account_groups(l)
1570
        
1571
        BaseTestCase.tearDown(self)
1540 1572
    
1541 1573
    def test_create_container(self):
1542 1574
        self.client.create_container('φάκελος')
1543 1575
        self.assert_container_exists('φάκελος')
1576
        
1577
        self.assertTrue('φάκελος' in self.client.list_containers())
1544 1578
    
1545 1579
    def test_create_object(self):
1546 1580
        self.client.create_container('φάκελος')
1547 1581
        self.upload_random_data('φάκελος', 'αντικείμενο')
1548 1582
        
1549 1583
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1584
        self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1550 1585
    
1551 1586
    def test_copy_object(self):
1552
        self.client.create_container('φάκελος')
1553
        self.upload_random_data('φάκελος', 'αντικείμενο')
1587
        src_container = 'φάκελος'
1588
        src_object = 'αντικείμενο'
1589
        dest_container = 'αντίγραφα'
1590
        dest_object = 'ασφαλές-αντίγραφο'
1554 1591
        
1555
        self.client.create_container('αντίγραφα')
1556
        self.client.copy_object('φάκελος', 'αντικείμενο', 'αντίγραφα',
1557
                                'αντικείμενο')
1592
        self.client.create_container(src_container)
1593
        self.upload_random_data(src_container, src_object)
1558 1594
        
1559
        self.assert_object_exists('αντίγραφα', 'αντικείμενο')
1560
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1595
        self.client.create_container(dest_container)
1596
        self.client.copy_object(src_container, src_object, dest_container,
1597
                                dest_object)
1598
        
1599
        self.assert_object_exists(src_container, src_object)
1600
        self.assert_object_exists(dest_container, dest_object)
1601
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1561 1602
    
1562 1603
    def test_move_object(self):
1563
        self.client.create_container('φάκελος')
1564
        self.upload_random_data('φάκελος', 'αντικείμενο')
1604
        src_container = 'φάκελος'
1605
        src_object = 'αντικείμενο'
1606
        dest_container = 'αντίγραφα'
1607
        dest_object = 'ασφαλές-αντίγραφο'
1565 1608
        
1566
        self.client.create_container('αντίγραφα')
1567
        self.client.copy_object('φάκελος', 'αντικείμενο', 'αντίγραφα',
1568
                                'αντικείμενο')
1609
        self.client.create_container(src_container)
1610
        self.upload_random_data(src_container, src_object)
1569 1611
        
1570
        self.assert_object_exists('αντίγραφα', 'αντικείμενο')
1571
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1612
        self.client.create_container(dest_container)
1613
        self.client.move_object(src_container, src_object, dest_container,
1614
                                dest_object)
1615
        
1616
        self.assert_object_not_exists(src_container, src_object)
1617
        self.assert_object_exists(dest_container, dest_object)
1618
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1572 1619
    
1573 1620
    def test_delete_object(self):
1574
        pass
1621
        self.client.create_container('φάκελος')
1622
        self.upload_random_data('φάκελος', 'αντικείμενο')
1623
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1575 1624
    
1576
    def test_delete_container(self):
1577
        pass
1625
        self.client.delete_object('φάκελος', 'αντικείμενο')
1626
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1627
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1578 1628
    
1629
    def test_delete_container(self):
1630
        self.client.create_container('φάκελος')
1631
        self.assert_container_exists('φάκελος')
1632
        
1633
        self.client.delete_container('φάκελος')
1634
        self.assert_container_not_exists('φάκελος')
1635
        self.assertTrue('φάκελος' not in self.client.list_containers())
1636

  
1579 1637
    def test_account_meta(self):
1580
        pass
1638
        meta = {'ποιότητα':'ΑΑΑ'}
1639
        self.client.update_account_metadata(**meta)
1640
        meta = self.client.retrieve_account_metadata(restricted=True)
1641
        self.assertTrue('ποιότητα' in meta.keys())
1642
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1581 1643
    
1582 1644
    def test_container_meta(self):
1583
        pass
1645
        meta = {'ποιότητα':'ΑΑΑ'}
1646
        self.client.create_container('φάκελος', **meta)
1647
        
1648
        meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1649
        self.assertTrue('ποιότητα' in meta.keys())
1650
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1584 1651
    
1585
    def test_obejct_meta(self):
1586
        pass
1652
    def test_object_meta(self):
1653
        self.client.create_container('φάκελος')
1654
        meta = {'ποιότητα':'ΑΑΑ'}
1655
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1656
        
1657
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1658
                                                    restricted=True)
1659
        self.assertTrue('ποιότητα' in meta.keys())
1660
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')    
1587 1661
    
1588 1662
    def test_list_meta_filtering(self):
1589
        pass
1663
        self.client.create_container('φάκελος')
1664
        meta = {'ποιότητα':'ΑΑΑ'}
1665
        self.upload_random_data('φάκελος', 'ο1', **meta)
1666
        self.upload_random_data('φάκελος', 'ο2')
1667
        self.upload_random_data('φάκελος', 'ο3')
1668
        
1669
        meta = {'ποσότητα':'μεγάλη'}
1670
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1671
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1672
        self.assertTrue('ο1' in objects)
1673
        self.assertTrue('ο2' in objects)
1674
        self.assertTrue('ο3' not in objects)
1590 1675
    
1591 1676
    def test_groups(self):
1592
        pass
1677
        #create a group
1678
        groups = {'σεφς':'chazapis,κούκης'}
1679
        self.client.set_account_groups(**groups)
1680
        groups.update(self.initial_groups)
1681
        self.assertEqual(groups['σεφς'],
1682
                         self.client.retrieve_account_groups()['σεφς'])
1683
        
1593 1684
    
1594 1685
    def test_permissions(self):
1595 1686
        pass
b/pithos/api/util.py
41 41
from django.http import HttpResponse
42 42
from django.utils import simplejson as json
43 43
from django.utils.http import http_date, parse_etags
44
from django.utils.encoding import smart_str
44 45

  
45 46
from pithos.api.compat import parse_http_date_safe, parse_http_date
46 47
from pithos.api.faults import (Fault, NotModified, BadRequest, Unauthorized, ItemNotFound,
......
77 78

  
78 79
def format_header_key(k):
79 80
    """Convert underscores to dashes and capitalize intra-dash strings."""
80
    
81 81
    return '-'.join([x.capitalize() for x in k.replace('_', '-').split('-')])
82 82

  
83 83
def get_header_prefix(request, prefix):
......
106 106
        response['X-Account-Bytes-Used'] = meta['bytes']
107 107
    response['Last-Modified'] = http_date(int(meta['modified']))
108 108
    for k in [x for x in meta.keys() if x.startswith('X-Account-Meta-')]:
109
        response[k.encode('utf-8')] = meta[k].encode('utf-8')
109
        response[smart_str(k, strings_only=True)] = smart_str(meta[k], strings_only=True)
110 110
    if 'until_timestamp' in meta:
111 111
        response['X-Account-Until-Timestamp'] = http_date(int(meta['until_timestamp']))
112 112
    for k, v in groups.iteritems():
113
        response[format_header_key('X-Account-Group-' + k).encode('utf-8')] = (','.join(v)).encode('utf-8')
114

  
113
        k = smart_str(k, strings_only=True)
114
        k = format_header_key('X-Account-Group-' + k)
115
        v = smart_str(','.join(v), strings_only=True)
116
        response[k] = v
117
    
115 118
def get_container_headers(request):
116 119
    meta = get_header_prefix(request, 'X-Container-Meta-')
117 120
    policy = dict([(k[19:].lower(), v.replace(' ', '')) for k, v in get_header_prefix(request, 'X-Container-Policy-').iteritems()])
......
124 127
        response['X-Container-Bytes-Used'] = meta['bytes']
125 128
    response['Last-Modified'] = http_date(int(meta['modified']))
126 129
    for k in [x for x in meta.keys() if x.startswith('X-Container-Meta-')]:
127
        response[k.encode('utf-8')] = meta[k].encode('utf-8')
128
    response['X-Container-Object-Meta'] = ','.join([x[14:] for x in meta['object_meta'] if x.startswith('X-Object-Meta-')])
130
        response[smart_str(k, strings_only=True)] = smart_str(meta[k], strings_only=True)
131
    l = [smart_str(x, strings_only=True) for x in meta['object_meta'] if x.startswith('X-Object-Meta-')]
132
    response['X-Container-Object-Meta'] = ','.join([x[14:] for x in l])
129 133
    response['X-Container-Block-Size'] = backend.block_size
130 134
    response['X-Container-Block-Hash'] = backend.hash_algorithm
131 135
    if 'until_timestamp' in meta:
132 136
        response['X-Container-Until-Timestamp'] = http_date(int(meta['until_timestamp']))
133 137
    for k, v in policy.iteritems():
134
        response[format_header_key('X-Container-Policy-' + k).encode('utf-8')] = v.encode('utf-8')
138
        response[smart_str(format_header_key('X-Container-Policy-' + k), strings_only=True)] = smart_str(v, strings_only=True)
135 139

  
136 140
def get_object_headers(request):
137 141
    meta = get_header_prefix(request, 'X-Object-Meta-')
......
155 159
        response['X-Object-Version'] = meta['version']
156 160
        response['X-Object-Version-Timestamp'] = http_date(int(meta['version_timestamp']))
157 161
        for k in [x for x in meta.keys() if x.startswith('X-Object-Meta-')]:
158
            response[k.encode('utf-8')] = meta[k].encode('utf-8')
162
            response[smart_str(k, strings_only=True)] = smart_str(meta[k], strings_only=True)
159 163
        for k in ('Content-Encoding', 'Content-Disposition', 'X-Object-Manifest', 'X-Object-Sharing', 'X-Object-Shared-By', 'X-Object-Public'):
160 164
            if k in meta:
161
                response[k] = meta[k]
165
                response[k] = smart_str(meta[k], strings_only=True)
162 166
    else:
163 167
        for k in ('Content-Encoding', 'Content-Disposition'):
164 168
            if k in meta:
b/pithos/lib/client.py
40 40
import socket
41 41
import urllib
42 42
import pithos.api.faults
43
import datetime
43 44

  
44 45
ERROR_CODES = {304:'Not Modified',
45 46
               400:'Bad Request',
......
73 74
    
74 75
    def _req(self, method, path, body=None, headers={}, format='text',
75 76
             params={}, top_level_req=False):
76
        path = urllib.quote(path)
77
        #path = urllib.quote(path)
77 78
        account = '' if top_level_req else self.account
78 79
        full_path = '/%s/%s%s?format=%s' % (self.api, account, path, format)
79 80
        
......
100 101
            kwargs['headers'].setdefault('content-type',
101 102
                                         'application/octet-stream')
102 103
        kwargs['headers'].setdefault('content-length', len(body) if body else 0)
103
        kwargs['headers'] = _encode_headers(kwargs['headers'])
104
        #kwargs['headers'] = _encode_headers(kwargs['headers'])
105
        
104 106
        #print '#', method, full_path, kwargs
107
        t1 = datetime.datetime.utcnow()
105 108
        conn.request(method, full_path, **kwargs)
106 109
        
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)
112
            
113 110
        resp = conn.getresponse()
111
        t2 = datetime.datetime.utcnow()
112
        #print 'response time:', str(t2-t1)
114 113
        headers = dict(resp.getheaders())
115 114
        
116 115
        if self.verbose:
......
126 125
            print
127 126
        
128 127
        if int(resp.status) in ERROR_CODES.keys():
129
            #print '**', resp.status
130 128
            raise Fault(data, int(resp.status))
131 129
        
132 130
        #print '**',  resp.status, headers, data
......
414 412
        return self._get_metadata(path, prefix, params=params)
415 413
    
416 414
    def update_object_metadata(self, container, object, **meta):
415
        """
416
        updates object's metadata
417
        """
417 418
        path = '/%s/%s' % (container, object)
418 419
        return self._update_metadata(path, 'object', **meta)
419 420
    
420 421
    def delete_object_metadata(self, container, object, meta=[]):
422
        """
423
        deletes object's metadata
424
        """
421 425
        path = '/%s/%s' % (container, object)
422 426
        return self._delete_metadata(path, 'object', meta)
423 427
    
......
562 566
        for k, v in groups.items():
563 567
            v = v.strip()
564 568
            headers['x-account-group-%s' % k] = v
565
        meta = self.retrieve_account_metadata()
566
        headers.update(meta)
569
        meta = self.retrieve_account_metadata(restricted=True)
570
        prefix = 'x-account-meta-'
571
        for k,v in meta.items():
572
            k = '%s%s' % (prefix, k)
573
            headers[k] = v
567 574
        return self.post('', headers=headers)
568 575
    
569 576
    # Storage Container Services
......
571 578
    def list_objects(self, container, format='text', limit=None, marker=None,
572 579
                     prefix=None, delimiter=None, path=None,
573 580
                     include_trashed=False, params={}, if_modified_since=None,
574
                     if_unmodified_since=None, meta={}, until=None):
581
                     if_unmodified_since=None, meta='', until=None):
575 582
        """returns a list with the container objects"""
576 583
        params = {'until':until, 'meta':meta}
577 584
        args = locals()
......
819 826
                                      **headers)
820 827
    
821 828
    def list_shared_by_others(self, limit=None, marker=None, format='text'):
829
         """lists other accounts that share objects to the user"""
822 830
         l = ['limit', 'marker']
823 831
         params = {}
824 832
         for elem in [elem for elem in l if eval(elem)]:
......
826 834
         return self._list('', format, params, top_level_req=True)
827 835
    
828 836
    def share_object(self, container, object, l, read=True):
837
        """gives access(read by default) to an object to a user/group list"""
829 838
        action = 'read' if read else 'write'
830 839
        sharing = '%s=%s' % (action, ','.join(l))
831 840
        self.update_object(container, object, f=None, x_object_sharing=sharing)

Also available in: Unified diff