Revision 67100dd2 snf-pithos-app/pithos/api/functions.py
b/snf-pithos-app/pithos/api/functions.py | ||
---|---|---|
40 | 40 |
from django.utils.encoding import smart_str |
41 | 41 |
from django.views.decorators.csrf import csrf_exempt |
42 | 42 |
|
43 |
from django.conf import settings |
|
44 | 43 |
from snf_django.lib.astakos import get_uuids as _get_uuids |
45 | 44 |
|
46 | 45 |
from snf_django.lib import api |
... | ... | |
57 | 56 |
get_content_range, socket_read_iterator, SaveToBackendHandler, |
58 | 57 |
object_data_response, put_object_block, hashmap_md5, simple_list_response, |
59 | 58 |
api_method, is_uuid, |
60 |
retrieve_uuid, retrieve_displayname, retrieve_uuids, retrieve_displaynames,
|
|
59 |
retrieve_uuid, retrieve_uuids, retrieve_displaynames, |
|
61 | 60 |
get_pithos_usage |
62 | 61 |
) |
63 | 62 |
|
... | ... | |
146 | 145 |
|
147 | 146 |
@csrf_exempt |
148 | 147 |
def object_demux(request, v_account, v_container, v_object): |
149 |
# Helper to avoid placing the token in the URL when loading objects from a browser. |
|
148 |
# Helper to avoid placing the token in the URL |
|
149 |
# when loading objects from a browser. |
|
150 | 150 |
if TRANSLATE_UUIDS: |
151 | 151 |
if not is_uuid(v_account): |
152 | 152 |
uuids = get_uuids([v_account]) |
... | ... | |
165 | 165 |
elif request.method == 'MOVE': |
166 | 166 |
return object_move(request, v_account, v_container, v_object) |
167 | 167 |
elif request.method == 'POST': |
168 |
if request.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'): |
|
168 |
if request.META.get( |
|
169 |
'CONTENT_TYPE', '').startswith('multipart/form-data'): |
|
169 | 170 |
return object_write_form(request, v_account, v_container, v_object) |
170 | 171 |
return object_update(request, v_account, v_container, v_object) |
171 | 172 |
elif request.method == 'DELETE': |
... | ... | |
214 | 215 |
if request.serialization == 'text': |
215 | 216 |
if TRANSLATE_UUIDS: |
216 | 217 |
accounts = retrieve_displaynames( |
217 |
getattr(request, 'token', None), accounts)
|
|
218 |
getattr(request, 'token', None), accounts) |
|
218 | 219 |
if len(accounts) == 0: |
219 | 220 |
# The cloudfiles python bindings expect 200 if json/xml. |
220 | 221 |
response.status_code = 204 |
... | ... | |
247 | 248 |
if TRANSLATE_UUIDS: |
248 | 249 |
uuids = list(d['name'] for d in account_meta) |
249 | 250 |
catalog = retrieve_displaynames( |
250 |
getattr(request, 'token', None), uuids, return_dict=True)
|
|
251 |
getattr(request, 'token', None), uuids, return_dict=True) |
|
251 | 252 |
for meta in account_meta: |
252 | 253 |
meta['name'] = catalog.get(meta.get('name')) |
253 | 254 |
|
... | ... | |
279 | 280 |
if TRANSLATE_UUIDS: |
280 | 281 |
for k in groups: |
281 | 282 |
groups[k] = retrieve_displaynames( |
282 |
getattr(request, 'token', None), groups[k])
|
|
283 |
getattr(request, 'token', None), groups[k]) |
|
283 | 284 |
policy = request.backend.get_account_policy( |
284 | 285 |
request.user_uniq, v_account, external_quota=usage) |
285 | 286 |
except NotAllowedError: |
... | ... | |
304 | 305 |
if TRANSLATE_UUIDS: |
305 | 306 |
try: |
306 | 307 |
groups[k] = retrieve_uuids( |
307 |
getattr(request, 'token', None),
|
|
308 |
groups[k],
|
|
309 |
fail_silently=False)
|
|
308 |
getattr(request, 'token', None), |
|
309 |
groups[k], |
|
310 |
fail_silently=False) |
|
310 | 311 |
except ItemNotExists, e: |
311 | 312 |
raise faults.BadRequest( |
312 |
'Bad X-Account-Group header value: %s' % e)
|
|
313 |
'Bad X-Account-Group header value: %s' % e) |
|
313 | 314 |
else: |
314 | 315 |
try: |
315 | 316 |
retrieve_displaynames( |
... | ... | |
318 | 319 |
fail_silently=False) |
319 | 320 |
except ItemNotExists, e: |
320 | 321 |
raise faults.BadRequest( |
321 |
'Bad X-Account-Group header value: %s' % e)
|
|
322 |
'Bad X-Account-Group header value: %s' % e) |
|
322 | 323 |
replace = True |
323 | 324 |
if 'update' in request.GET: |
324 | 325 |
replace = False |
... | ... | |
437 | 438 |
try: |
438 | 439 |
meta = request.backend.get_container_meta(request.user_uniq, v_account, |
439 | 440 |
v_container, 'pithos', until) |
440 |
meta['object_meta'] = request.backend.list_container_meta(request.user_uniq, |
|
441 |
v_account, v_container, 'pithos', until) |
|
441 |
meta['object_meta'] = \ |
|
442 |
request.backend.list_container_meta(request.user_uniq, |
|
443 |
v_account, v_container, |
|
444 |
'pithos', until) |
|
442 | 445 |
policy = request.backend.get_container_policy( |
443 | 446 |
request.user_uniq, v_account, |
444 | 447 |
v_container) |
... | ... | |
489 | 492 |
if meta: |
490 | 493 |
try: |
491 | 494 |
request.backend.update_container_meta(request.user_uniq, v_account, |
492 |
v_container, 'pithos', meta, replace=False) |
|
495 |
v_container, 'pithos', |
|
496 |
meta, replace=False) |
|
493 | 497 |
except NotAllowedError: |
494 | 498 |
raise faults.Forbidden('Not allowed') |
495 | 499 |
except ItemNotExists: |
... | ... | |
524 | 528 |
if meta or replace: |
525 | 529 |
try: |
526 | 530 |
request.backend.update_container_meta(request.user_uniq, v_account, |
527 |
v_container, 'pithos', meta, replace) |
|
531 |
v_container, 'pithos', |
|
532 |
meta, replace) |
|
528 | 533 |
except NotAllowedError: |
529 | 534 |
raise faults.Forbidden('Not allowed') |
530 | 535 |
except ItemNotExists: |
... | ... | |
536 | 541 |
request.META.get('CONTENT_LENGTH', 0)) |
537 | 542 |
content_type = request.META.get('CONTENT_TYPE') |
538 | 543 |
hashmap = [] |
539 |
if content_type and content_type == 'application/octet-stream' and content_length != 0: |
|
544 |
if (content_type |
|
545 |
and content_type == 'application/octet-stream' |
|
546 |
and content_length != 0): |
|
540 | 547 |
for data in socket_read_iterator(request, content_length, |
541 | 548 |
request.backend.block_size): |
542 | 549 |
# TODO: Raise 408 (Request Timeout) if this takes too long. |
543 |
# TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data. |
|
550 |
# TODO: Raise 499 (Client Disconnect) if a length is defined |
|
551 |
# and we stop before getting this much data. |
|
544 | 552 |
hashmap.append(request.backend.put_block(data)) |
545 | 553 |
|
546 | 554 |
response = HttpResponse(status=202) |
... | ... | |
590 | 598 |
try: |
591 | 599 |
meta = request.backend.get_container_meta(request.user_uniq, v_account, |
592 | 600 |
v_container, 'pithos', until) |
593 |
meta['object_meta'] = request.backend.list_container_meta(request.user_uniq, |
|
594 |
v_account, v_container, 'pithos', until) |
|
601 |
meta['object_meta'] = \ |
|
602 |
request.backend.list_container_meta(request.user_uniq, |
|
603 |
v_account, v_container, |
|
604 |
'pithos', until) |
|
595 | 605 |
policy = request.backend.get_container_policy( |
596 | 606 |
request.user_uniq, v_account, |
597 | 607 |
v_container) |
... | ... | |
679 | 689 |
if until is None: |
680 | 690 |
name = '/'.join((v_account, v_container, '')) |
681 | 691 |
name_idx = len(name) |
682 |
for x in request.backend.list_object_permissions(request.user_uniq,
|
|
683 |
v_account, v_container, prefix):
|
|
692 |
for x in request.backend.list_object_permissions( |
|
693 |
request.user_uniq, v_account, v_container, prefix):
|
|
684 | 694 |
|
685 | 695 |
# filter out objects which are not under the container |
686 | 696 |
if name != x[:name_idx]: |
687 | 697 |
continue |
688 | 698 |
|
689 | 699 |
object = x[name_idx:] |
690 |
object_permissions[object] = request.backend.get_object_permissions( |
|
691 |
request.user_uniq, v_account, v_container, object) |
|
700 |
object_permissions[object] = \ |
|
701 |
request.backend.get_object_permissions( |
|
702 |
request.user_uniq, v_account, v_container, object) |
|
692 | 703 |
|
693 | 704 |
if public_granted: |
694 | 705 |
for k, v in request.backend.list_object_public( |
... | ... | |
706 | 717 |
modified_by = meta.get('modified_by') |
707 | 718 |
if modified_by: |
708 | 719 |
l = retrieve_displaynames( |
709 |
getattr(request, 'token', None), [meta['modified_by']])
|
|
720 |
getattr(request, 'token', None), [meta['modified_by']]) |
|
710 | 721 |
if l is not None and len(l) == 1: |
711 | 722 |
meta['modified_by'] = l[0] |
712 | 723 |
|
... | ... | |
757 | 768 |
version = request.GET.get('version') |
758 | 769 |
try: |
759 | 770 |
meta = request.backend.get_object_meta(request.user_uniq, v_account, |
760 |
v_container, v_object, 'pithos', version) |
|
771 |
v_container, v_object, |
|
772 |
'pithos', version) |
|
761 | 773 |
if version is None: |
762 | 774 |
permissions = request.backend.get_object_permissions( |
763 | 775 |
request.user_uniq, |
... | ... | |
833 | 845 |
|
834 | 846 |
try: |
835 | 847 |
meta = request.backend.get_object_meta(request.user_uniq, v_account, |
836 |
v_container, v_object, 'pithos', version) |
|
848 |
v_container, v_object, |
|
849 |
'pithos', version) |
|
837 | 850 |
if version is None: |
838 | 851 |
permissions = request.backend.get_object_permissions( |
839 | 852 |
request.user_uniq, |
... | ... | |
888 | 901 |
|
889 | 902 |
try: |
890 | 903 |
for x in objects: |
891 |
s, h = request.backend.get_object_hashmap(request.user_uniq, |
|
892 |
v_account, src_container, x[0], x[1]) |
|
904 |
s, h = \ |
|
905 |
request.backend.get_object_hashmap(request.user_uniq, |
|
906 |
v_account, src_container, |
|
907 |
x[0], x[1]) |
|
893 | 908 |
sizes.append(s) |
894 | 909 |
hashmaps.append(h) |
895 | 910 |
except NotAllowedError: |
... | ... | |
929 | 944 |
|
930 | 945 |
response = HttpResponse(data, status=200) |
931 | 946 |
put_object_headers( |
932 |
response, meta, token=getattr(request, 'token', None))
|
|
947 |
response, meta, token=getattr(request, 'token', None)) |
|
933 | 948 |
response['Content-Length'] = len(data) |
934 | 949 |
return response |
935 | 950 |
|
... | ... | |
951 | 966 |
# requestentitytoolarge (413) |
952 | 967 |
|
953 | 968 |
# Evaluate conditions. |
954 |
if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'): |
|
969 |
if (request.META.get('HTTP_IF_MATCH') |
|
970 |
or request.META.get('HTTP_IF_NONE_MATCH')): |
|
955 | 971 |
try: |
956 | 972 |
meta = request.backend.get_object_meta( |
957 | 973 |
request.user_uniq, v_account, |
... | ... | |
988 | 1004 |
raise faults.BadRequest('Invalid X-Move-From header') |
989 | 1005 |
version_id = copy_or_move_object( |
990 | 1006 |
request, src_account, src_container, src_name, |
991 |
v_account, v_container, v_object, move=True, delimiter=delimiter) |
|
1007 |
v_account, v_container, v_object, |
|
1008 |
move=True, delimiter=delimiter) |
|
992 | 1009 |
else: |
993 | 1010 |
try: |
994 | 1011 |
src_container, src_name = split_container_object_string( |
... | ... | |
997 | 1014 |
raise faults.BadRequest('Invalid X-Copy-From header') |
998 | 1015 |
version_id = copy_or_move_object( |
999 | 1016 |
request, src_account, src_container, src_name, |
1000 |
v_account, v_container, v_object, move=False, delimiter=delimiter) |
|
1017 |
v_account, v_container, v_object, |
|
1018 |
move=False, delimiter=delimiter) |
|
1001 | 1019 |
response = HttpResponse(status=201) |
1002 | 1020 |
response['X-Object-Version'] = version_id |
1003 | 1021 |
return response |
... | ... | |
1049 | 1067 |
for data in socket_read_iterator(request, content_length, |
1050 | 1068 |
request.backend.block_size): |
1051 | 1069 |
# TODO: Raise 408 (Request Timeout) if this takes too long. |
1052 |
# TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data. |
|
1070 |
# TODO: Raise 499 (Client Disconnect) if a length is defined |
|
1071 |
# and we stop before getting this much data. |
|
1053 | 1072 |
size += len(data) |
1054 | 1073 |
hashmap.append(request.backend.put_block(data)) |
1055 | 1074 |
md5.update(data) |
... | ... | |
1060 | 1079 |
raise faults.UnprocessableEntity('Object ETag does not match') |
1061 | 1080 |
|
1062 | 1081 |
try: |
1063 |
version_id = request.backend.update_object_hashmap(request.user_uniq, |
|
1064 |
v_account, v_container, v_object, size, content_type, |
|
1065 |
hashmap, checksum, 'pithos', meta, True, permissions) |
|
1082 |
version_id = \ |
|
1083 |
request.backend.update_object_hashmap(request.user_uniq, |
|
1084 |
v_account, v_container, |
|
1085 |
v_object, size, content_type, |
|
1086 |
hashmap, checksum, |
|
1087 |
'pithos', meta, True, |
|
1088 |
permissions) |
|
1066 | 1089 |
except NotAllowedError: |
1067 | 1090 |
raise faults.Forbidden('Not allowed') |
1068 | 1091 |
except IndexError, e: |
... | ... | |
1081 | 1104 |
checksum = hashmap_md5(request.backend, hashmap, size) |
1082 | 1105 |
try: |
1083 | 1106 |
request.backend.update_object_checksum(request.user_uniq, |
1084 |
v_account, v_container, v_object, version_id, checksum) |
|
1107 |
v_account, v_container, |
|
1108 |
v_object, version_id, |
|
1109 |
checksum) |
|
1085 | 1110 |
except NotAllowedError: |
1086 | 1111 |
raise faults.Forbidden('Not allowed') |
1087 | 1112 |
if public is not None: |
... | ... | |
1116 | 1141 |
|
1117 | 1142 |
checksum = file.etag |
1118 | 1143 |
try: |
1119 |
version_id = request.backend.update_object_hashmap(request.user_uniq, |
|
1120 |
v_account, v_container, v_object, file.size, file.content_type, |
|
1121 |
file.hashmap, checksum, 'pithos', {}, True) |
|
1144 |
version_id = \ |
|
1145 |
request.backend.update_object_hashmap(request.user_uniq, |
|
1146 |
v_account, v_container, |
|
1147 |
v_object, file.size, |
|
1148 |
file.content_type, |
|
1149 |
file.hashmap, checksum, |
|
1150 |
'pithos', {}, True) |
|
1122 | 1151 |
except NotAllowedError: |
1123 | 1152 |
raise faults.Forbidden('Not allowed') |
1124 | 1153 |
except ItemNotExists: |
... | ... | |
1154 | 1183 |
raise faults.BadRequest('Invalid Destination header') |
1155 | 1184 |
|
1156 | 1185 |
# Evaluate conditions. |
1157 |
if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'): |
|
1186 |
if (request.META.get('HTTP_IF_MATCH') |
|
1187 |
or request.META.get('HTTP_IF_NONE_MATCH')): |
|
1158 | 1188 |
src_version = request.META.get('HTTP_X_SOURCE_VERSION') |
1159 | 1189 |
try: |
1160 | 1190 |
meta = request.backend.get_object_meta( |
... | ... | |
1169 | 1199 |
delimiter = request.GET.get('delimiter') |
1170 | 1200 |
|
1171 | 1201 |
version_id = copy_or_move_object(request, v_account, v_container, v_object, |
1172 |
dest_account, dest_container, dest_name, move=False, delimiter=delimiter) |
|
1202 |
dest_account, dest_container, dest_name, |
|
1203 |
move=False, delimiter=delimiter) |
|
1173 | 1204 |
response = HttpResponse(status=201) |
1174 | 1205 |
response['X-Object-Version'] = version_id |
1175 | 1206 |
return response |
... | ... | |
1196 | 1227 |
raise faults.BadRequest('Invalid Destination header') |
1197 | 1228 |
|
1198 | 1229 |
# Evaluate conditions. |
1199 |
if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'): |
|
1230 |
if (request.META.get('HTTP_IF_MATCH') |
|
1231 |
or request.META.get('HTTP_IF_NONE_MATCH')): |
|
1200 | 1232 |
try: |
1201 | 1233 |
meta = request.backend.get_object_meta( |
1202 | 1234 |
request.user_uniq, v_account, |
... | ... | |
1210 | 1242 |
delimiter = request.GET.get('delimiter') |
1211 | 1243 |
|
1212 | 1244 |
version_id = copy_or_move_object(request, v_account, v_container, v_object, |
1213 |
dest_account, dest_container, dest_name, move=True, delimiter=delimiter) |
|
1245 |
dest_account, dest_container, dest_name, |
|
1246 |
move=True, delimiter=delimiter) |
|
1214 | 1247 |
response = HttpResponse(status=201) |
1215 | 1248 |
response['X-Object-Version'] = version_id |
1216 | 1249 |
return response |
... | ... | |
1237 | 1270 |
raise faults.ItemNotFound('Object does not exist') |
1238 | 1271 |
|
1239 | 1272 |
# Evaluate conditions. |
1240 |
if request.META.get('HTTP_IF_MATCH') or request.META.get('HTTP_IF_NONE_MATCH'): |
|
1273 |
if (request.META.get('HTTP_IF_MATCH') |
|
1274 |
or request.META.get('HTTP_IF_NONE_MATCH')): |
|
1241 | 1275 |
validate_matching_preconditions(request, prev_meta) |
1242 | 1276 |
|
1243 | 1277 |
replace = True |
... | ... | |
1246 | 1280 |
|
1247 | 1281 |
# A Content-Type or X-Source-Object header indicates data updates. |
1248 | 1282 |
src_object = request.META.get('HTTP_X_SOURCE_OBJECT') |
1249 |
if (not content_type or content_type != 'application/octet-stream') and not src_object: |
|
1283 |
if ((not content_type or content_type != 'application/octet-stream') |
|
1284 |
and not src_object): |
|
1250 | 1285 |
response = HttpResponse(status=202) |
1251 | 1286 |
|
1252 | 1287 |
# Do permissions first, as it may fail easier. |
1253 | 1288 |
if permissions is not None: |
1254 | 1289 |
try: |
1255 | 1290 |
request.backend.update_object_permissions(request.user_uniq, |
1256 |
v_account, v_container, v_object, permissions) |
|
1291 |
v_account, |
|
1292 |
v_container, v_object, |
|
1293 |
permissions) |
|
1257 | 1294 |
except NotAllowedError: |
1258 | 1295 |
raise faults.Forbidden('Not allowed') |
1259 | 1296 |
except ItemNotExists: |
... | ... | |
1293 | 1330 |
raise faults.RangeNotSatisfiable('Invalid Content-Range header') |
1294 | 1331 |
|
1295 | 1332 |
try: |
1296 |
size, hashmap = request.backend.get_object_hashmap(request.user_uniq, |
|
1297 |
v_account, v_container, v_object) |
|
1333 |
size, hashmap = \ |
|
1334 |
request.backend.get_object_hashmap(request.user_uniq, |
|
1335 |
v_account, v_container, v_object) |
|
1298 | 1336 |
except NotAllowedError: |
1299 | 1337 |
raise faults.Forbidden('Not allowed') |
1300 | 1338 |
except ItemNotExists: |
... | ... | |
1304 | 1342 |
if offset is None: |
1305 | 1343 |
offset = size |
1306 | 1344 |
elif offset > size: |
1307 |
raise faults.RangeNotSatisfiable('Supplied offset is beyond object limits') |
|
1345 |
raise faults.RangeNotSatisfiable( |
|
1346 |
'Supplied offset is beyond object limits') |
|
1308 | 1347 |
if src_object: |
1309 | 1348 |
src_account = request.META.get('HTTP_X_SOURCE_ACCOUNT') |
1310 | 1349 |
if not src_account: |
... | ... | |
1323 | 1362 |
if length is None: |
1324 | 1363 |
length = src_size |
1325 | 1364 |
elif length > src_size: |
1326 |
raise faults.BadRequest('Object length is smaller than range length') |
|
1365 |
raise faults.BadRequest( |
|
1366 |
'Object length is smaller than range length') |
|
1327 | 1367 |
else: |
1328 | 1368 |
# Require either a Content-Length, or 'chunked' Transfer-Encoding. |
1329 | 1369 |
content_length = -1 |
... | ... | |
1337 | 1377 |
# TODO: Get up to length bytes in chunks. |
1338 | 1378 |
length = content_length |
1339 | 1379 |
elif length != content_length: |
1340 |
raise faults.BadRequest('Content length does not match range length') |
|
1341 |
if total is not None and (total != size or offset >= size or (length > 0 and offset + length >= size)): |
|
1380 |
raise faults.BadRequest( |
|
1381 |
'Content length does not match range length') |
|
1382 |
if (total is not None |
|
1383 |
and (total != size or offset >= size |
|
1384 |
or (length > 0 and offset + length >= size))): |
|
1342 | 1385 |
raise faults.RangeNotSatisfiable( |
1343 | 1386 |
'Supplied range will change provided object limits') |
1344 | 1387 |
|
... | ... | |
1384 | 1427 |
for d in socket_read_iterator(request, length, |
1385 | 1428 |
request.backend.block_size): |
1386 | 1429 |
# TODO: Raise 408 (Request Timeout) if this takes too long. |
1387 |
# TODO: Raise 499 (Client Disconnect) if a length is defined and we stop before getting this much data. |
|
1430 |
# TODO: Raise 499 (Client Disconnect) if a length is defined |
|
1431 |
# and we stop before getting this much data. |
|
1388 | 1432 |
data += d |
1389 | 1433 |
bytes = put_object_block(request, hashmap, data, offset) |
1390 | 1434 |
offset += bytes |
... | ... | |
1400 | 1444 |
checksum = hashmap_md5( |
1401 | 1445 |
request.backend, hashmap, size) if UPDATE_MD5 else '' |
1402 | 1446 |
try: |
1403 |
version_id = request.backend.update_object_hashmap(request.user_uniq, |
|
1404 |
v_account, v_container, v_object, size, prev_meta[ |
|
1405 |
'type'], |
|
1406 |
hashmap, checksum, 'pithos', meta, replace, permissions) |
|
1447 |
version_id = \ |
|
1448 |
request.backend.update_object_hashmap(request.user_uniq, |
|
1449 |
v_account, v_container, |
|
1450 |
v_object, size, |
|
1451 |
prev_meta['type'], |
|
1452 |
hashmap, checksum, 'pithos', |
|
1453 |
meta, replace, permissions) |
|
1407 | 1454 |
except NotAllowedError: |
1408 | 1455 |
raise faults.Forbidden('Not allowed') |
1409 | 1456 |
except ItemNotExists: |
Also available in: Unified diff