Revision f7ab99df pithos/lib/client.py
b/pithos/lib/client.py | ||
---|---|---|
34 | 34 |
self.api = api |
35 | 35 |
self.verbose = verbose or debug |
36 | 36 |
self.debug = debug |
37 |
|
|
37 |
|
|
38 | 38 |
def _chunked_transfer(self, path, method='PUT', f=stdin, headers=None, |
39 | 39 |
blocksize=1024): |
40 | 40 |
http = HTTPConnection(self.host) |
... | ... | |
81 | 81 |
print '%s: %s' % (key.capitalize(), val) |
82 | 82 |
|
83 | 83 |
|
84 |
length = hasattr(headers, 'Content-length') \ |
|
85 |
and headers['Content-length'] or None |
|
86 |
|
|
84 |
length = resp.getheader('Content-length', None) |
|
87 | 85 |
data = resp.read(length) |
88 | 86 |
if self.debug: |
89 | 87 |
print data |
... | ... | |
92 | 90 |
if int(resp.status) in ERROR_CODES.keys(): |
93 | 91 |
raise Fault(data, int(resp.status)) |
94 | 92 |
|
93 |
#print '*', resp.status, headers, data |
|
95 | 94 |
return resp.status, headers, data |
96 |
|
|
95 |
|
|
97 | 96 |
def req(self, method, path, body=None, headers=None, format='text', |
98 | 97 |
params=None): |
99 | 98 |
full_path = '/%s/%s%s?format=%s' % (self.api, self.account, path, |
... | ... | |
131 | 130 |
print '%s: %s' % (key.capitalize(), val) |
132 | 131 |
|
133 | 132 |
|
134 |
length = hasattr(headers, 'Content-length') \ |
|
135 |
and headers['Content-length'] or None |
|
136 |
|
|
133 |
length = resp.getheader('Content-length', None) |
|
137 | 134 |
data = resp.read(length) |
138 | 135 |
if self.debug: |
139 | 136 |
print data |
... | ... | |
144 | 141 |
|
145 | 142 |
#print '*', resp.status, headers, data |
146 | 143 |
return resp.status, headers, data |
147 |
|
|
144 |
|
|
148 | 145 |
def delete(self, path, format='text'): |
149 | 146 |
return self.req('DELETE', path, format=format) |
150 |
|
|
147 |
|
|
151 | 148 |
def get(self, path, format='text', headers=None, params=None): |
152 | 149 |
return self.req('GET', path, headers=headers, format=format, |
153 | 150 |
params=params) |
154 |
|
|
151 |
|
|
155 | 152 |
def head(self, path, format='text', params=None): |
156 | 153 |
return self.req('HEAD', path, format=format, params=params) |
157 |
|
|
154 |
|
|
158 | 155 |
def post(self, path, body=None, format='text', headers=None): |
159 | 156 |
return self.req('POST', path, body, headers=headers, format=format) |
160 |
|
|
157 |
|
|
161 | 158 |
def put(self, path, body=None, format='text', headers=None): |
162 | 159 |
return self.req('PUT', path, body, headers=headers, format=format) |
163 |
|
|
160 |
|
|
164 | 161 |
def _list(self, path, detail=False, params=None, headers=None): |
165 | 162 |
format = 'json' if detail else 'text' |
166 | 163 |
status, headers, data = self.get(path, format=format, headers=headers, |
... | ... | |
170 | 167 |
else: |
171 | 168 |
data = data.strip().split('\n') |
172 | 169 |
return data |
173 |
|
|
170 |
|
|
174 | 171 |
def _get_metadata(self, path, prefix=None, params=None): |
175 | 172 |
status, headers, data = self.head(path, params=params) |
176 |
prefixlen = prefix and len(prefix) or 0
|
|
173 |
prefixlen = len(prefix) if prefix else 0
|
|
177 | 174 |
meta = {} |
178 | 175 |
for key, val in headers.items(): |
179 | 176 |
if prefix and not key.startswith(prefix): |
... | ... | |
182 | 179 |
key = key[prefixlen:] |
183 | 180 |
meta[key] = val |
184 | 181 |
return meta |
185 |
|
|
186 |
def _set_metadata(self, path, entity, **meta): |
|
187 |
headers = {} |
|
182 |
|
|
183 |
def _update_metadata(self, path, entity, **meta): |
|
184 |
""" |
|
185 |
adds new and updates the values of previously set metadata |
|
186 |
""" |
|
188 | 187 |
for key, val in meta.items(): |
189 |
http_key = 'X-%s-Meta-%s' %(entity.capitalize(), key.capitalize()) |
|
188 |
meta.pop(key) |
|
189 |
meta['X-%s-Meta-%s' %(entity.capitalize(), key.capitalize())] = val |
|
190 |
prev_meta = self._get_metadata(path) |
|
191 |
prev_meta.update(meta) |
|
192 |
headers = {} |
|
193 |
for key, val in prev_meta.items(): |
|
194 |
headers[key.capitalize()] = val |
|
195 |
self.post(path, headers=headers) |
|
196 |
|
|
197 |
def _delete_metadata(self, path, entity, meta=[]): |
|
198 |
""" |
|
199 |
delete previously set metadata |
|
200 |
""" |
|
201 |
prev_meta = self._get_metadata(path) |
|
202 |
headers = {} |
|
203 |
for key, val in prev_meta.items(): |
|
204 |
if key.split('-')[-1] in meta: |
|
205 |
continue |
|
206 |
http_key = key.capitalize() |
|
190 | 207 |
headers[http_key] = val |
191 | 208 |
self.post(path, headers=headers) |
192 |
|
|
209 |
|
|
193 | 210 |
# Storage Account Services |
194 |
|
|
211 |
|
|
195 | 212 |
def list_containers(self, detail=False, params=None, headers=None): |
196 | 213 |
return self._list('', detail, params, headers) |
197 |
|
|
214 |
|
|
198 | 215 |
def account_metadata(self, restricted=False, until=None): |
199 |
prefix = restricted and 'x-account-meta-' or None
|
|
200 |
params = until and {'until':until} or None
|
|
216 |
prefix = 'x-account-meta-' if restricted else None
|
|
217 |
params = {'until':until} if until else None
|
|
201 | 218 |
return self._get_metadata('', prefix, params=params) |
202 |
|
|
219 |
|
|
203 | 220 |
def update_account_metadata(self, **meta): |
204 |
self._set_metadata('', 'account', **meta) |
|
205 |
|
|
221 |
self._update_metadata('', 'account', **meta) |
|
222 |
|
|
223 |
def delete_account_metadata(self, meta=[]): |
|
224 |
self._delete_metadata('', 'account', meta) |
|
225 |
|
|
206 | 226 |
# Storage Container Services |
207 |
|
|
227 |
|
|
208 | 228 |
def list_objects(self, container, detail=False, params=None, headers=None): |
209 | 229 |
return self._list('/' + container, detail, params, headers) |
210 |
|
|
230 |
|
|
211 | 231 |
def create_container(self, container, headers=None): |
212 | 232 |
status, header, data = self.put('/' + container, headers=headers) |
213 | 233 |
if status == 202: |
... | ... | |
215 | 235 |
elif status != 201: |
216 | 236 |
raise Fault(data, int(status)) |
217 | 237 |
return True |
218 |
|
|
238 |
|
|
219 | 239 |
def delete_container(self, container): |
220 | 240 |
self.delete('/' + container) |
221 |
|
|
241 |
|
|
222 | 242 |
def retrieve_container_metadata(self, container, restricted=False, |
223 | 243 |
until=None): |
224 |
prefix = restricted and 'x-container-meta-' or None
|
|
225 |
params = until and {'until':until} or None
|
|
244 |
prefix = 'x-container-meta-' if restricted else None
|
|
245 |
params = {'until':until} if until else None
|
|
226 | 246 |
return self._get_metadata('/%s' % container, prefix, params=params) |
227 |
|
|
247 |
|
|
228 | 248 |
def update_container_metadata(self, container, **meta): |
229 |
self._set_metadata('/' + container, 'container', **meta) |
|
230 |
|
|
249 |
self._update_metadata('/' + container, 'container', **meta) |
|
250 |
|
|
251 |
def delete_container_metadata(self, container, meta=[]): |
|
252 |
path = '/%s' % (container) |
|
253 |
self._delete_metadata(path, 'container', meta) |
|
254 |
|
|
231 | 255 |
# Storage Object Services |
232 |
|
|
256 |
|
|
233 | 257 |
def retrieve_object(self, container, object, detail=False, headers=None, |
234 | 258 |
version=None): |
235 | 259 |
path = '/%s/%s' % (container, object) |
236 | 260 |
format = 'json' if detail else 'text' |
237 |
params = version and {'version':version} or None
|
|
261 |
params = {'version':version} if version else None
|
|
238 | 262 |
status, headers, data = self.get(path, format, headers, params) |
239 | 263 |
return data |
240 |
|
|
264 |
|
|
265 |
def create_directory_marker(self, container, object): |
|
266 |
if not object: |
|
267 |
raise Fault('Directory markers have to be nested in a container') |
|
268 |
h = {'Content-Type':'application/directory'} |
|
269 |
self.create_object(container, object, f=None, headers=h) |
|
270 |
|
|
241 | 271 |
def create_object(self, container, object, f=stdin, chunked=False, |
242 | 272 |
blocksize=1024, headers=None): |
243 | 273 |
""" |
... | ... | |
247 | 277 |
""" |
248 | 278 |
path = '/%s/%s' % (container, object) |
249 | 279 |
if not chunked and f != stdin: |
250 |
data = f and f.read() or None
|
|
280 |
data = f.read() if f else None
|
|
251 | 281 |
return self.put(path, data, headers=headers) |
252 | 282 |
else: |
253 | 283 |
return self._chunked_transfer(path, 'PUT', f, headers=headers, |
254 | 284 |
blocksize=1024) |
255 |
|
|
285 |
|
|
256 | 286 |
def update_object(self, container, object, f=stdin, chunked=False, |
257 | 287 |
blocksize=1024, headers=None): |
258 | 288 |
if not f: |
... | ... | |
264 | 294 |
else: |
265 | 295 |
self._chunked_transfer(path, 'POST', f, headers=headers, |
266 | 296 |
blocksize=1024) |
267 |
|
|
297 |
|
|
268 | 298 |
def _change_obj_location(self, src_container, src_object, dst_container, |
269 | 299 |
dst_object, remove=False, headers=None): |
270 | 300 |
path = '/%s/%s' % (dst_container, dst_object) |
... | ... | |
276 | 306 |
headers['X-Copy-From'] = '/%s/%s' % (src_container, src_object) |
277 | 307 |
headers['Content-Length'] = 0 |
278 | 308 |
self.put(path, headers=headers) |
279 |
|
|
309 |
|
|
280 | 310 |
def copy_object(self, src_container, src_object, dst_container, |
281 | 311 |
dst_object, headers=None): |
282 | 312 |
self._change_obj_location(src_container, src_object, |
283 | 313 |
dst_container, dst_object, |
284 | 314 |
headers=headers) |
285 |
|
|
315 |
|
|
286 | 316 |
def move_object(self, src_container, src_object, dst_container, |
287 | 317 |
dst_object, headers=None): |
288 | 318 |
self._change_obj_location(src_container, src_object, |
289 | 319 |
dst_container, dst_object, True, headers) |
290 |
|
|
320 |
|
|
291 | 321 |
def delete_object(self, container, object): |
292 | 322 |
self.delete('/%s/%s' % (container, object)) |
293 |
|
|
323 |
|
|
294 | 324 |
def retrieve_object_metadata(self, container, object, restricted=False, |
295 | 325 |
version=None): |
296 | 326 |
path = '/%s/%s' % (container, object) |
297 |
prefix = restricted and 'x-object-meta-' or None
|
|
298 |
params = version and {'version':version} or None
|
|
327 |
prefix = 'x-object-meta-' if restricted else None
|
|
328 |
params = {'version':version} if version else None
|
|
299 | 329 |
return self._get_metadata(path, prefix, params=params) |
300 |
|
|
330 |
|
|
301 | 331 |
def update_object_metadata(self, container, object, **meta): |
302 | 332 |
path = '/%s/%s' % (container, object) |
303 |
self._set_metadata(path, 'object', **meta) |
|
333 |
self._update_metadata(path, 'object', **meta) |
|
334 |
|
|
335 |
def delete_object_metadata(self, container, object, meta=[]): |
|
336 |
path = '/%s/%s' % (container, object) |
|
337 |
self._delete_metadata(path, 'object', meta) |
|
338 |
|
|
339 |
def trash_object(self, container, object): |
|
340 |
""" |
|
341 |
trashes an object |
|
342 |
actually resets all object metadata with trash = true |
|
343 |
""" |
|
344 |
path = '/%s/%s' % (container, object) |
|
345 |
meta = {'trash':'true'} |
|
346 |
self._update_metadata(path, 'object', **meta) |
|
347 |
|
|
348 |
def restore_object(self, container, object): |
|
349 |
""" |
|
350 |
restores a trashed object |
|
351 |
actualy just resets all object metadata except trash |
|
352 |
""" |
|
353 |
self.delete_object_metadata(container, object, ['trash']) |
|
354 |
|
Also available in: Unified diff