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