Revision ec1b8d3e

b/pithos/lib/client.py
1
from httplib import HTTPConnection
1
from httplib import HTTPConnection, HTTP
2
from sys import stdin
2 3

  
3 4
import json
5
import types
4 6

  
5 7
class Fault(Exception):
6 8
    def __init__(self, data=''):
......
18 20
        self.verbose = verbose or debug
19 21
        self.debug = debug
20 22
    
23
    def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None,
24
                          blocksize=1024):
25
        http = HTTPConnection(self.host)
26
        #http = HTTP()
27
        #http.connect('127.0.0.1', 8000)
28
        
29
        # write header
30
        path = '/%s/%s%s' % (self.api, self.account, path)
31
        http.putrequest(method, path)
32
        http.putheader('Content-Type', 'application/octet-stream')
33
        http.putheader('Transfer-Encoding', 'chunked')
34
        if headers:
35
            for header,value in headers.items():
36
                http.putheader(header, value)
37
        http.endheaders()
38
        
39
        # write body
40
        data = ''
41
        while True:
42
            if f.closed:
43
                break
44
            block = f.read(blocksize)
45
            if block == '':
46
                break
47
            data = '%s\r\n%s\r\n' % (hex(len(block)), block)
48
            try:
49
                http.send(data)
50
            except:
51
                #retry
52
                http.send(data)
53
        data = '0x0\r\n'
54
        try:
55
            http.send(data)
56
        except:
57
            #retry
58
            http.send(data)
59
        
60
        # get response
61
        resp = http.getresponse()
62
        headers = dict(resp.getheaders())
63
        
64
        if self.verbose:
65
            print '%d %s' % (resp.status, resp.reason)
66
            for key, val in headers.items():
67
                print '%s: %s' % (key.capitalize(), val)
68
            print
69
        
70
        data = resp.read()
71
        if self.debug:
72
            print data
73
            print
74
        
75
        return resp.status, headers, data
76

  
21 77
    def req(self, method, path, body=None, headers=None, format='text', params=None):
22 78
        full_path = '/%s/%s%s?format=%s' % (self.api, self.account, path, format)
23 79
        if params:
24 80
            for k,v in params.items():
25
                full_path = '%s&%s=%s' %(full_path, k, v)
81
                if v:
82
                    full_path = '%s&%s=%s' %(full_path, k, v)
26 83
        conn = HTTPConnection(self.host)
27 84
        
85
        
28 86
        kwargs = {}
29 87
        kwargs['headers'] = headers or {}
30
        kwargs['headers']['Content-Length'] = len(body) if body else 0
88
        if not headers or \
89
        'Transfer-Encoding' not in headers \
90
        or headers['Transfer-Encoding'] != 'chunked':
91
            kwargs['headers']['Content-Length'] = len(body) if body else 0
31 92
        if body:
32 93
            kwargs['body'] = body
33 94
            kwargs['headers']['Content-Type'] = 'application/octet-stream'
34
        
35
        conn.request(method, full_path, params, **kwargs)
95
        #print '****', method, full_path, kwargs
96
        conn.request(method, full_path, **kwargs)
36 97
        resp = conn.getresponse()
37 98
        headers = dict(resp.getheaders())
38 99
        
......
43 104
            print
44 105
        
45 106
        data = resp.read()
46
        
47 107
        if self.debug:
48 108
            print data
49 109
            print
50 110
        
51
        if format == 'json':
52
            data = json.loads(data)
53
        
54 111
        return resp.status, headers, data
55
    
112

  
56 113
    def delete(self, path, format='text'):
57 114
        return self.req('DELETE', path, format=format)
58
    
115

  
59 116
    def get(self, path, format='text', headers=None, params=None):
60 117
        return self.req('GET', path, headers=headers, format=format,
61
                        params=None)
62
    
118
                        params=params)
119

  
63 120
    def head(self, path, format='text'):
64 121
        return self.req('HEAD', path, format=format)
65
    
122

  
66 123
    def post(self, path, body=None, format='text', headers=None):
67 124
        return self.req('POST', path, body, headers=headers, format=format)
68
    
125

  
69 126
    def put(self, path, body=None, format='text', headers=None):
70 127
        return self.req('PUT', path, body, headers=headers, format=format)
71
    
128

  
72 129
    def _list(self, path, detail=False, params=None, headers=None):
73 130
        format = 'json' if detail else 'text'
74 131
        status, headers, data = self.get(path, format=format, headers=headers,
75 132
                                         params=params)
133
        if detail:
134
            data = json.loads(data)
135
        else:
136
            data = [data]
76 137
        return data
77
    
138

  
78 139
    def _get_metadata(self, path, prefix):
79 140
        status, headers, data = self.head(path)
141
        if status == '404':
142
            return None
80 143
        prefixlen = len(prefix)
81 144
        meta = {}
82 145
        for key, val in headers.items():
......
84 147
                key = key[prefixlen:]
85 148
                meta[key] = val
86 149
        return meta
87
    
150

  
88 151
    def _set_metadata(self, path, entity, **meta):
89 152
        headers = {}
90 153
        for key, val in meta.items():
......
93 156
        self.post(path, headers=headers)
94 157

  
95 158
    # Storage Account Services
96
    
97
    def list_containers(self, detail=False, limit=1000, marker=None,
98
                        if_modified_since=None, if_unmodified_since=None):
99
        d = {}
100
        if if_modified_since:
101
            d['IF_MODIFIED_SINCE'] = if_modified_since
102
        if if_unmodified_since:
103
            d['IF_UNMODIFIED_SINCE'] = if_unmodified_since
104
        headers = d and d or None
105
        params = {'limit':limit, 'marker':marker}
159

  
160
    def list_containers(self, detail=False, params=None, headers=None):
106 161
        return self._list('', detail, params, headers)
107
    
162

  
108 163
    def account_metadata(self):
109
        return self._get_metadata('', 'x-account-')
164
        return self._get_metadata('', 'x-account-meta-')
110 165

  
111 166
    def update_account_metadata(self, **meta):
112 167
        self._set_metadata('', 'account', **meta)
113
    
168

  
114 169
    # Storage Container Services
115
    
116
    def list_objects(self, container, detail=False, limit=1000, marker=None,
117
                     prefix=None, delimiter=None, path=None, meta=None,
118
                     if_modified_since=None, if_unmodified_since=None):
119
        params = locals()
120
        params.pop('container')
121
        params.pop('detail')
122
        return self._list('/' + container, detail)
123
    
124
    def create_container(self, container):
125
        status, header, data = self.put('/' + container)
170

  
171
    def list_objects(self, container, detail=False, params=None, headers=None):
172
        return self._list('/' + container, detail, params, headers)
173

  
174
    def create_container(self, container, headers=None):
175
        status, header, data = self.put('/' + container, headers=headers)
126 176
        if status == 202:
127 177
            return False
128 178
        elif status != 201:
129 179
            raise Fault(data)
130 180
        return True
131
    
181

  
132 182
    def delete_container(self, container):
133 183
        self.delete('/' + container)
134
    
184

  
135 185
    def retrieve_container_metadata(self, container):
136
        return self._get_metadata('/%s' % container, 'x-container-')
186
        return self._get_metadata('/%s' % container, 'x-container-meta-')
137 187

  
138 188
    def update_container_metadata(self, container, **meta):
139 189
        self._set_metadata('/' + container, 'container', **meta)
140
    
190

  
141 191
    # Storage Object Services
142
    
143
    def retrieve_object(self, container, object):
192

  
193
    def retrieve_object(self, container, object, detail=False, headers=None):
144 194
        path = '/%s/%s' % (container, object)
145
        status, headers, data = self.get(path)
195
        format = 'json' if detail else 'text'
196
        status, headers, data = self.get(path, format, headers)
146 197
        return data
147
    
148
    def create_object(self, container, object, data):
198

  
199
    def create_object(self, container, object, f=stdin, chunked=False,
200
                      blocksize=1024, headers=None):
201
        if not f:
202
            return
149 203
        path = '/%s/%s' % (container, object)
150
        self.put(path, data)
151
    
152
    def copy_object(self, src_container, src_object, dst_container, dst_object):
204
        if not chunked and f != stdin:
205
            data = f.read()
206
            self.put(path, data, headers=headers)
207
        else:
208
            self._chunked_transfer(path, 'PUT', f, headers=headers,
209
                                   blocksize=1024)
210

  
211
    def update_object(self, container, object, f=stdin, chunked=False,
212
                      blocksize=1024, headers=None):
213
        if not f:
214
            return
215
        path = '/%s/%s' % (container, object)
216
        if not chunked and f != stdin:
217
            data = f.read()
218
            self.post(path, data, headers=headers)
219
        else:
220
            self._chunked_transfer(path, 'POST', f, headers=headers,
221
                                   blocksize=1024)
222

  
223
    def _change_obj_location(self, src_container, src_object, dst_container,
224
                             dst_object, remove=False):
153 225
        path = '/%s/%s' % (dst_container, dst_object)
154 226
        headers = {}
155
        headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object)
227
        if remove:
228
            headers['X-Move-From'] = '/%s/%s' % (src_container, src_object)
229
        else:
230
            headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object)
156 231
        headers['Content-Length'] = 0
157 232
        self.put(path, headers=headers)
158
    
233

  
234
    def copy_object(self, src_container, src_object, dst_container,
235
                             dst_object):
236
        self._change_obj_location(src_container, src_object,
237
                                   dst_container, dst_object)
238

  
239
    def move_object(self, src_container, src_object, dst_container,
240
                             dst_object):
241
        self._change_obj_location(src_container, src_object,
242
                                   dst_container, dst_object, True)
243

  
159 244
    def delete_object(self, container, object):
160 245
        self.delete('/%s/%s' % (container, object))
161
    
246

  
162 247
    def retrieve_object_metadata(self, container, object):
163 248
        path = '/%s/%s' % (container, object)
164 249
        return self._get_metadata(path, 'x-object-meta-')
165
    
250

  
166 251
    def update_object_metadata(self, container, object, **meta):
167 252
        path = '/%s/%s' % (container, object)
168 253
        self._set_metadata(path, 'object', **meta)

Also available in: Unified diff