Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: boto/s3/bucket.py

Issue 8386013: Merging in latest boto. (Closed) Base URL: svn://svn.chromium.org/boto
Patch Set: Redoing vendor drop by deleting and then merging. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « boto/s3/acl.py ('k') | boto/s3/bucketlistresultset.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ 1 # Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
2 # Copyright (c) 2010, Eucalyptus Systems, Inc. 2 # Copyright (c) 2010, Eucalyptus Systems, Inc.
3 # All rights reserved. 3 # All rights reserved.
4 # 4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a 5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the 6 # copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including 7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish, dis- 8 # without limitation the rights to use, copy, modify, merge, publish, dis-
9 # tribute, sublicense, and/or sell copies of the Software, and to permit 9 # tribute, sublicense, and/or sell copies of the Software, and to permit
10 # persons to whom the Software is furnished to do so, subject to the fol- 10 # persons to whom the Software is furnished to do so, subject to the fol-
11 # lowing conditions: 11 # lowing conditions:
12 # 12 #
13 # The above copyright notice and this permission notice shall be included 13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software. 14 # in all copies or substantial portions of the Software.
15 # 15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 18 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 19 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 # IN THE SOFTWARE. 22 # IN THE SOFTWARE.
23 23
24 import boto 24 import boto
25 from boto import handler 25 from boto import handler
26 from boto.provider import Provider
27 from boto.resultset import ResultSet 26 from boto.resultset import ResultSet
28 from boto.s3.acl import ACL, Policy, CannedACLStrings, Grant 27 from boto.s3.acl import Policy, CannedACLStrings, Grant
29 from boto.s3.key import Key 28 from boto.s3.key import Key
30 from boto.s3.prefix import Prefix 29 from boto.s3.prefix import Prefix
31 from boto.s3.deletemarker import DeleteMarker 30 from boto.s3.deletemarker import DeleteMarker
32 from boto.s3.user import User
33 from boto.s3.multipart import MultiPartUpload 31 from boto.s3.multipart import MultiPartUpload
34 from boto.s3.multipart import CompleteMultiPartUpload 32 from boto.s3.multipart import CompleteMultiPartUpload
35 from boto.s3.bucketlistresultset import BucketListResultSet 33 from boto.s3.bucketlistresultset import BucketListResultSet
36 from boto.s3.bucketlistresultset import VersionedBucketListResultSet 34 from boto.s3.bucketlistresultset import VersionedBucketListResultSet
37 from boto.s3.bucketlistresultset import MultiPartUploadListResultSet 35 from boto.s3.bucketlistresultset import MultiPartUploadListResultSet
38 import boto.jsonresponse 36 import boto.jsonresponse
39 import boto.utils 37 import boto.utils
40 import xml.sax 38 import xml.sax
41 import urllib 39 import urllib
42 import re 40 import re
43 from collections import defaultdict 41 from collections import defaultdict
44 42
45 # as per http://goo.gl/BDuud (02/19/2011) 43 # as per http://goo.gl/BDuud (02/19/2011)
46 class S3WebsiteEndpointTranslate: 44 class S3WebsiteEndpointTranslate:
47 trans_region = defaultdict(lambda :'s3-website-us-east-1') 45 trans_region = defaultdict(lambda :'s3-website-us-east-1')
48 46
49 trans_region['EU'] = 's3-website-eu-west-1' 47 trans_region['EU'] = 's3-website-eu-west-1'
50 trans_region['us-west-1'] = 's3-website-us-west-1' 48 trans_region['us-west-1'] = 's3-website-us-west-1'
49 trans_region['ap-northeast-1'] = 's3-website-ap-northeast-1'
51 trans_region['ap-southeast-1'] = 's3-website-ap-southeast-1' 50 trans_region['ap-southeast-1'] = 's3-website-ap-southeast-1'
52 51
53 @classmethod 52 @classmethod
54 def translate_region(self, reg): 53 def translate_region(self, reg):
55 return self.trans_region[reg] 54 return self.trans_region[reg]
56 55
57 S3Permissions = ['READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'] 56 S3Permissions = ['READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL']
58 57
59 class Bucket(object): 58 class Bucket(object):
60 59
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 self.connection = connection 98 self.connection = connection
100 self.key_class = key_class 99 self.key_class = key_class
101 100
102 def __repr__(self): 101 def __repr__(self):
103 return '<Bucket: %s>' % self.name 102 return '<Bucket: %s>' % self.name
104 103
105 def __iter__(self): 104 def __iter__(self):
106 return iter(BucketListResultSet(self)) 105 return iter(BucketListResultSet(self))
107 106
108 def __contains__(self, key_name): 107 def __contains__(self, key_name):
109 return not (self.get_key(key_name) is None) 108 return not (self.get_key(key_name) is None)
110 109
111 def startElement(self, name, attrs, connection): 110 def startElement(self, name, attrs, connection):
112 return None 111 return None
113 112
114 def endElement(self, name, value, connection): 113 def endElement(self, name, value, connection):
115 if name == 'Name': 114 if name == 'Name':
116 self.name = value 115 self.name = value
117 elif name == 'CreationDate': 116 elif name == 'CreationDate':
118 self.creation_date = value 117 self.creation_date = value
119 else: 118 else:
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 # support Range gets, which return status 206: 167 # support Range gets, which return status 206:
169 if response.status/100 == 2: 168 if response.status/100 == 2:
170 response.read() 169 response.read()
171 k = self.key_class(self) 170 k = self.key_class(self)
172 provider = self.connection.provider 171 provider = self.connection.provider
173 k.metadata = boto.utils.get_aws_metadata(response.msg, provider) 172 k.metadata = boto.utils.get_aws_metadata(response.msg, provider)
174 k.etag = response.getheader('etag') 173 k.etag = response.getheader('etag')
175 k.content_type = response.getheader('content-type') 174 k.content_type = response.getheader('content-type')
176 k.content_encoding = response.getheader('content-encoding') 175 k.content_encoding = response.getheader('content-encoding')
177 k.last_modified = response.getheader('last-modified') 176 k.last_modified = response.getheader('last-modified')
178 k.size = int(response.getheader('content-length')) 177 # the following machinations are a workaround to the fact that
178 # apache/fastcgi omits the content-length header on HEAD
179 # requests when the content-length is zero.
180 # See http://goo.gl/0Tdax for more details.
181 clen = response.getheader('content-length')
182 if clen:
183 k.size = int(response.getheader('content-length'))
184 else:
185 k.size = 0
179 k.cache_control = response.getheader('cache-control') 186 k.cache_control = response.getheader('cache-control')
180 k.name = key_name 187 k.name = key_name
181 k.handle_version_headers(response) 188 k.handle_version_headers(response)
189 k.handle_encryption_headers(response)
182 return k 190 return k
183 else: 191 else:
184 if response.status == 404: 192 if response.status == 404:
185 response.read() 193 response.read()
186 return None 194 return None
187 else: 195 else:
188 raise self.connection.provider.storage_response_error( 196 raise self.connection.provider.storage_response_error(
189 response.status, response.reason, '') 197 response.status, response.reason, '')
190 198
191 def list(self, prefix='', delimiter='', marker='', headers=None): 199 def list(self, prefix='', delimiter='', marker='', headers=None):
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 :rtype: :class:`boto.s3.bucketlistresultset.BucketListResultSet` 282 :rtype: :class:`boto.s3.bucketlistresultset.BucketListResultSet`
275 :return: an instance of a BucketListResultSet that handles paging, etc 283 :return: an instance of a BucketListResultSet that handles paging, etc
276 """ 284 """
277 return MultiPartUploadListResultSet(self, key_marker, 285 return MultiPartUploadListResultSet(self, key_marker,
278 upload_id_marker, 286 upload_id_marker,
279 headers) 287 headers)
280 288
281 def _get_all(self, element_map, initial_query_string='', 289 def _get_all(self, element_map, initial_query_string='',
282 headers=None, **params): 290 headers=None, **params):
283 l = [] 291 l = []
284 for k,v in params.items(): 292 for k, v in params.items():
285 k = k.replace('_', '-') 293 k = k.replace('_', '-')
286 if k == 'maxkeys': 294 if k == 'maxkeys':
287 k = 'max-keys' 295 k = 'max-keys'
288 if isinstance(v, unicode): 296 if isinstance(v, unicode):
289 v = v.encode('utf-8') 297 v = v.encode('utf-8')
290 if v is not None and v != '': 298 if v is not None and v != '':
291 l.append('%s=%s' % (urllib.quote(k), urllib.quote(str(v)))) 299 l.append('%s=%s' % (urllib.quote(k), urllib.quote(str(v))))
292 if len(l): 300 if len(l):
293 s = initial_query_string + '&' + '&'.join(l) 301 s = initial_query_string + '&' + '&'.join(l)
294 else: 302 else:
295 s = initial_query_string 303 s = initial_query_string
296 response = self.connection.make_request('GET', self.name, 304 response = self.connection.make_request('GET', self.name,
297 headers=headers, query_args=s) 305 headers=headers,
306 query_args=s)
298 body = response.read() 307 body = response.read()
299 boto.log.debug(body) 308 boto.log.debug(body)
300 if response.status == 200: 309 if response.status == 200:
301 rs = ResultSet(element_map) 310 rs = ResultSet(element_map)
302 h = handler.XmlHandler(rs, self) 311 h = handler.XmlHandler(rs, self)
303 xml.sax.parseString(body, h) 312 xml.sax.parseString(body, h)
304 return rs 313 return rs
305 else: 314 else:
306 raise self.connection.provider.storage_response_error( 315 raise self.connection.provider.storage_response_error(
307 response.status, response.reason, body) 316 response.status, response.reason, body)
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 Creates a new key 436 Creates a new key
428 437
429 :type key_name: string 438 :type key_name: string
430 :param key_name: The name of the key to create 439 :param key_name: The name of the key to create
431 440
432 :rtype: :class:`boto.s3.key.Key` or subclass 441 :rtype: :class:`boto.s3.key.Key` or subclass
433 :returns: An instance of the newly created key object 442 :returns: An instance of the newly created key object
434 """ 443 """
435 return self.key_class(self, key_name) 444 return self.key_class(self, key_name)
436 445
437 def generate_url(self, expires_in, method='GET', 446 def generate_url(self, expires_in, method='GET', headers=None,
438 headers=None, force_http=False): 447 force_http=False, response_headers=None):
439 return self.connection.generate_url(expires_in, method, self.name, 448 return self.connection.generate_url(expires_in, method, self.name,
440 headers=headers, 449 headers=headers,
441 force_http=force_http) 450 force_http=force_http,
451 response_headers=response_headers)
442 452
443 def delete_key(self, key_name, headers=None, 453 def delete_key(self, key_name, headers=None,
444 version_id=None, mfa_token=None): 454 version_id=None, mfa_token=None):
445 """ 455 """
446 Deletes a key from the bucket. If a version_id is provided, 456 Deletes a key from the bucket. If a version_id is provided,
447 only that version of the key will be deleted. 457 only that version of the key will be deleted.
448 458
449 :type key_name: string 459 :type key_name: string
450 :param key_name: The key name to delete 460 :param key_name: The key name to delete
451 461
(...skipping 20 matching lines...) Expand all
472 response = self.connection.make_request('DELETE', self.name, key_name, 482 response = self.connection.make_request('DELETE', self.name, key_name,
473 headers=headers, 483 headers=headers,
474 query_args=query_args) 484 query_args=query_args)
475 body = response.read() 485 body = response.read()
476 if response.status != 204: 486 if response.status != 204:
477 raise provider.storage_response_error(response.status, 487 raise provider.storage_response_error(response.status,
478 response.reason, body) 488 response.reason, body)
479 489
480 def copy_key(self, new_key_name, src_bucket_name, 490 def copy_key(self, new_key_name, src_bucket_name,
481 src_key_name, metadata=None, src_version_id=None, 491 src_key_name, metadata=None, src_version_id=None,
482 storage_class='STANDARD', preserve_acl=False): 492 storage_class='STANDARD', preserve_acl=False,
493 encrypt_key=False):
483 """ 494 """
484 Create a new key in the bucket by copying another existing key. 495 Create a new key in the bucket by copying another existing key.
485 496
486 :type new_key_name: string 497 :type new_key_name: string
487 :param new_key_name: The name of the new key 498 :param new_key_name: The name of the new key
488 499
489 :type src_bucket_name: string 500 :type src_bucket_name: string
490 :param src_bucket_name: The name of the source bucket 501 :param src_bucket_name: The name of the source bucket
491 502
492 :type src_key_name: string 503 :type src_key_name: string
(...skipping 24 matching lines...) Expand all
517 will have the default ACL. 528 will have the default ACL.
518 Note that preserving the ACL in the 529 Note that preserving the ACL in the
519 new key object will require two 530 new key object will require two
520 additional API calls to S3, one to 531 additional API calls to S3, one to
521 retrieve the current ACL and one to 532 retrieve the current ACL and one to
522 set that ACL on the new object. If 533 set that ACL on the new object. If
523 you don't care about the ACL, a value 534 you don't care about the ACL, a value
524 of False will be significantly more 535 of False will be significantly more
525 efficient. 536 efficient.
526 537
538 :type encrypt_key: bool
539 :param encrypt_key: If True, the new copy of the object will
540 be encrypted on the server-side by S3 and
541 will be stored in an encrypted form while
542 at rest in S3.
543
527 :rtype: :class:`boto.s3.key.Key` or subclass 544 :rtype: :class:`boto.s3.key.Key` or subclass
528 :returns: An instance of the newly created key object 545 :returns: An instance of the newly created key object
529 """ 546 """
547 headers = {}
548 provider = self.connection.provider
549 src_key_name = boto.utils.get_utf8_value(src_key_name)
530 if preserve_acl: 550 if preserve_acl:
531 acl = self.get_xml_acl(src_key_name) 551 if self.name == src_bucket_name:
552 src_bucket = self
553 else:
554 src_bucket = self.connection.get_bucket(src_bucket_name)
555 acl = src_bucket.get_xml_acl(src_key_name)
556 if encrypt_key:
557 headers[provider.server_side_encryption_header] = 'AES256'
532 src = '%s/%s' % (src_bucket_name, urllib.quote(src_key_name)) 558 src = '%s/%s' % (src_bucket_name, urllib.quote(src_key_name))
533 if src_version_id: 559 if src_version_id:
534 src += '?version_id=%s' % src_version_id 560 src += '?version_id=%s' % src_version_id
535 provider = self.connection.provider 561 headers = {provider.copy_source_header : str(src)}
536 headers = {provider.copy_source_header : src} 562 headers[provider.storage_class_header] = storage_class
537 if storage_class != 'STANDARD':
538 headers[provider.storage_class_header] = storage_class
539 if metadata: 563 if metadata:
540 headers[provider.metadata_directive_header] = 'REPLACE' 564 headers[provider.metadata_directive_header] = 'REPLACE'
541 headers = boto.utils.merge_meta(headers, metadata) 565 headers = boto.utils.merge_meta(headers, metadata, provider)
542 else: 566 else:
543 headers[provider.metadata_directive_header] = 'COPY' 567 headers[provider.metadata_directive_header] = 'COPY'
544 response = self.connection.make_request('PUT', self.name, new_key_name, 568 response = self.connection.make_request('PUT', self.name, new_key_name,
545 headers=headers) 569 headers=headers)
546 body = response.read() 570 body = response.read()
547 if response.status == 200: 571 if response.status == 200:
548 key = self.new_key(new_key_name) 572 key = self.new_key(new_key_name)
549 h = handler.XmlHandler(key, self) 573 h = handler.XmlHandler(key, self)
550 xml.sax.parseString(body, h) 574 xml.sax.parseString(body, h)
551 if hasattr(key, 'Error'): 575 if hasattr(key, 'Error'):
552 raise provider.storage_copy_error(key.Code, key.Message, body) 576 raise provider.storage_copy_error(key.Code, key.Message, body)
553 key.handle_version_headers(response) 577 key.handle_version_headers(response)
554 if preserve_acl: 578 if preserve_acl:
555 self.set_xml_acl(acl, new_key_name) 579 self.set_xml_acl(acl, new_key_name)
556 return key 580 return key
557 else: 581 else:
558 raise provider.storage_response_error(response.status, response.reas on, body) 582 raise provider.storage_response_error(response.status,
583 response.reason, body)
559 584
560 def set_canned_acl(self, acl_str, key_name='', headers=None, 585 def set_canned_acl(self, acl_str, key_name='', headers=None,
561 version_id=None): 586 version_id=None):
562 assert acl_str in CannedACLStrings 587 assert acl_str in CannedACLStrings
563 588
564 if headers: 589 if headers:
565 headers[self.connection.provider.acl_header] = acl_str 590 headers[self.connection.provider.acl_header] = acl_str
566 else: 591 else:
567 headers={self.connection.provider.acl_header: acl_str} 592 headers={self.connection.provider.acl_header: acl_str}
568 593
569 query_args='acl' 594 query_args = 'acl'
570 if version_id: 595 if version_id:
571 query_args += '&versionId=%s' % version_id 596 query_args += '&versionId=%s' % version_id
572 response = self.connection.make_request('PUT', self.name, key_name, 597 response = self.connection.make_request('PUT', self.name, key_name,
573 headers=headers, query_args=query_args) 598 headers=headers, query_args=query_args)
574 body = response.read() 599 body = response.read()
575 if response.status != 200: 600 if response.status != 200:
576 raise self.connection.provider.storage_response_error( 601 raise self.connection.provider.storage_response_error(
577 response.status, response.reason, body) 602 response.status, response.reason, body)
578 603
579 def get_xml_acl(self, key_name='', headers=None, version_id=None): 604 def get_xml_acl(self, key_name='', headers=None, version_id=None):
580 query_args = 'acl' 605 query_args = 'acl'
581 if version_id: 606 if version_id:
582 query_args += '&versionId=%s' % version_id 607 query_args += '&versionId=%s' % version_id
583 response = self.connection.make_request('GET', self.name, key_name, 608 response = self.connection.make_request('GET', self.name, key_name,
584 query_args=query_args, 609 query_args=query_args,
585 headers=headers) 610 headers=headers)
586 body = response.read() 611 body = response.read()
587 if response.status != 200: 612 if response.status != 200:
588 raise self.connection.provider.storage_response_error( 613 raise self.connection.provider.storage_response_error(
589 response.status, response.reason, body) 614 response.status, response.reason, body)
590 return body 615 return body
591 616
592 def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None): 617 def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None):
593 query_args = 'acl' 618 query_args = 'acl'
594 if version_id: 619 if version_id:
595 query_args += '&versionId=%s' % version_id 620 query_args += '&versionId=%s' % version_id
596 response = self.connection.make_request('PUT', self.name, key_name, 621 response = self.connection.make_request('PUT', self.name, key_name,
597 data=acl_str, 622 data=acl_str.encode('ISO-8859-1' ),
598 query_args=query_args, 623 query_args=query_args,
599 headers=headers) 624 headers=headers)
600 body = response.read() 625 body = response.read()
601 if response.status != 200: 626 if response.status != 200:
602 raise self.connection.provider.storage_response_error( 627 raise self.connection.provider.storage_response_error(
603 response.status, response.reason, body) 628 response.status, response.reason, body)
604 629
605 def set_acl(self, acl_or_str, key_name='', headers=None, version_id=None): 630 def set_acl(self, acl_or_str, key_name='', headers=None, version_id=None):
606 if isinstance(acl_or_str, Policy): 631 if isinstance(acl_or_str, Policy):
607 self.set_xml_acl(acl_or_str.to_xml(), key_name, 632 self.set_xml_acl(acl_or_str.to_xml(), key_name,
(...skipping 12 matching lines...) Expand all
620 body = response.read() 645 body = response.read()
621 if response.status == 200: 646 if response.status == 200:
622 policy = Policy(self) 647 policy = Policy(self)
623 h = handler.XmlHandler(policy, self) 648 h = handler.XmlHandler(policy, self)
624 xml.sax.parseString(body, h) 649 xml.sax.parseString(body, h)
625 return policy 650 return policy
626 else: 651 else:
627 raise self.connection.provider.storage_response_error( 652 raise self.connection.provider.storage_response_error(
628 response.status, response.reason, body) 653 response.status, response.reason, body)
629 654
655 def set_subresource(self, subresource, value, key_name = '', headers=None,
656 version_id=None):
657 """
658 Set a subresource for a bucket or key.
659
660 :type subresource: string
661 :param subresource: The subresource to set.
662
663 :type value: string
664 :param value: The value of the subresource.
665
666 :type key_name: string
667 :param key_name: The key to operate on, or None to operate on the
668 bucket.
669
670 :type headers: dict
671 :param headers: Additional HTTP headers to include in the request.
672
673 :type src_version_id: string
674 :param src_version_id: Optional. The version id of the key to operate
675 on. If not specified, operate on the newest
676 version.
677 """
678 if not subresource:
679 raise TypeError('set_subresource called with subresource=None')
680 query_args = subresource
681 if version_id:
682 query_args += '&versionId=%s' % version_id
683 response = self.connection.make_request('PUT', self.name, key_name,
684 data=value.encode('UTF-8'),
685 query_args=query_args,
686 headers=headers)
687 body = response.read()
688 if response.status != 200:
689 raise self.connection.provider.storage_response_error(
690 response.status, response.reason, body)
691
692 def get_subresource(self, subresource, key_name='', headers=None,
693 version_id=None):
694 """
695 Get a subresource for a bucket or key.
696
697 :type subresource: string
698 :param subresource: The subresource to get.
699
700 :type key_name: string
701 :param key_name: The key to operate on, or None to operate on the
702 bucket.
703
704 :type headers: dict
705 :param headers: Additional HTTP headers to include in the request.
706
707 :type src_version_id: string
708 :param src_version_id: Optional. The version id of the key to operate
709 on. If not specified, operate on the newest
710 version.
711
712 :rtype: string
713 :returns: The value of the subresource.
714 """
715 if not subresource:
716 raise TypeError('get_subresource called with subresource=None')
717 query_args = subresource
718 if version_id:
719 query_args += '&versionId=%s' % version_id
720 response = self.connection.make_request('GET', self.name, key_name,
721 query_args=query_args,
722 headers=headers)
723 body = response.read()
724 if response.status != 200:
725 raise self.connection.provider.storage_response_error(
726 response.status, response.reason, body)
727 return body
728
630 def make_public(self, recursive=False, headers=None): 729 def make_public(self, recursive=False, headers=None):
631 self.set_canned_acl('public-read', headers=headers) 730 self.set_canned_acl('public-read', headers=headers)
632 if recursive: 731 if recursive:
633 for key in self: 732 for key in self:
634 self.set_canned_acl('public-read', key.name, headers=headers) 733 self.set_canned_acl('public-read', key.name, headers=headers)
635 734
636 def add_email_grant(self, permission, email_address, 735 def add_email_grant(self, permission, email_address,
637 recursive=False, headers=None): 736 recursive=False, headers=None):
638 """ 737 """
639 Convenience method that provides a quick way to add an email grant 738 Convenience method that provides a quick way to add an email grant
(...skipping 21 matching lines...) Expand all
661 if permission not in S3Permissions: 760 if permission not in S3Permissions:
662 raise self.connection.provider.storage_permissions_error( 761 raise self.connection.provider.storage_permissions_error(
663 'Unknown Permission: %s' % permission) 762 'Unknown Permission: %s' % permission)
664 policy = self.get_acl(headers=headers) 763 policy = self.get_acl(headers=headers)
665 policy.acl.add_email_grant(permission, email_address) 764 policy.acl.add_email_grant(permission, email_address)
666 self.set_acl(policy, headers=headers) 765 self.set_acl(policy, headers=headers)
667 if recursive: 766 if recursive:
668 for key in self: 767 for key in self:
669 key.add_email_grant(permission, email_address, headers=headers) 768 key.add_email_grant(permission, email_address, headers=headers)
670 769
671 def add_user_grant(self, permission, user_id, 770 def add_user_grant(self, permission, user_id, recursive=False,
672 recursive=False, headers=None): 771 headers=None, display_name=None):
673 """ 772 """
674 Convenience method that provides a quick way to add a canonical 773 Convenience method that provides a quick way to add a canonical
675 user grant to a bucket. This method retrieves the current ACL, 774 user grant to a bucket. This method retrieves the current ACL,
676 creates a new grant based on the parameters passed in, adds that 775 creates a new grant based on the parameters passed in, adds that
677 grant to the ACL and then PUT's the new ACL back to S3. 776 grant to the ACL and then PUT's the new ACL back to S3.
678 777
679 :type permission: string 778 :type permission: string
680 :param permission: The permission being granted. Should be one of: 779 :param permission: The permission being granted. Should be one of:
681 (READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL). 780 (READ, WRITE, READ_ACP, WRITE_ACP, FULL_CONTROL).
682 781
683 :type user_id: string 782 :type user_id: string
684 :param user_id: The canonical user id associated with the AWS 783 :param user_id: The canonical user id associated with the AWS
685 account your are granting the permission to. 784 account your are granting the permission to.
686 785
687 :type recursive: boolean 786 :type recursive: boolean
688 :param recursive: A boolean value to controls whether the command 787 :param recursive: A boolean value to controls whether the command
689 will apply the grant to all keys within the bucket 788 will apply the grant to all keys within the bucket
690 or not. The default value is False. By passing a 789 or not. The default value is False. By passing a
691 True value, the call will iterate through all keys 790 True value, the call will iterate through all keys
692 in the bucket and apply the same grant to each key. 791 in the bucket and apply the same grant to each key.
693 CAUTION: If you have a lot of keys, this could take 792 CAUTION: If you have a lot of keys, this could take
694 a long time! 793 a long time!
794
795 :type display_name: string
796 :param display_name: An option string containing the user's
797 Display Name. Only required on Walrus.
695 """ 798 """
696 if permission not in S3Permissions: 799 if permission not in S3Permissions:
697 raise self.connection.provider.storage_permissions_error( 800 raise self.connection.provider.storage_permissions_error(
698 'Unknown Permission: %s' % permission) 801 'Unknown Permission: %s' % permission)
699 policy = self.get_acl(headers=headers) 802 policy = self.get_acl(headers=headers)
700 policy.acl.add_user_grant(permission, user_id) 803 policy.acl.add_user_grant(permission, user_id,
804 display_name=display_name)
701 self.set_acl(policy, headers=headers) 805 self.set_acl(policy, headers=headers)
702 if recursive: 806 if recursive:
703 for key in self: 807 for key in self:
704 key.add_user_grant(permission, user_id, headers=headers) 808 key.add_user_grant(permission, user_id, headers=headers,
809 display_name=display_name)
705 810
706 def list_grants(self, headers=None): 811 def list_grants(self, headers=None):
707 policy = self.get_acl(headers=headers) 812 policy = self.get_acl(headers=headers)
708 return policy.acl.grants 813 return policy.acl.grants
709 814
710 def get_location(self): 815 def get_location(self):
711 """ 816 """
712 Returns the LocationConstraint for the bucket. 817 Returns the LocationConstraint for the bucket.
713 818
714 :rtype: str 819 :rtype: str
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
788 if response.status == 200: 893 if response.status == 200:
789 return True 894 return True
790 else: 895 else:
791 raise self.connection.provider.storage_response_error( 896 raise self.connection.provider.storage_response_error(
792 response.status, response.reason, body) 897 response.status, response.reason, body)
793 898
794 def configure_versioning(self, versioning, mfa_delete=False, 899 def configure_versioning(self, versioning, mfa_delete=False,
795 mfa_token=None, headers=None): 900 mfa_token=None, headers=None):
796 """ 901 """
797 Configure versioning for this bucket. 902 Configure versioning for this bucket.
798 Note: This feature is currently in beta release and is available 903
799 only in the Northern California region. 904 ..note:: This feature is currently in beta.
800 905
801 :type versioning: bool 906 :type versioning: bool
802 :param versioning: A boolean indicating whether version is 907 :param versioning: A boolean indicating whether version is
803 enabled (True) or disabled (False). 908 enabled (True) or disabled (False).
804 909
805 :type mfa_delete: bool 910 :type mfa_delete: bool
806 :param mfa_delete: A boolean indicating whether the Multi-Factor 911 :param mfa_delete: A boolean indicating whether the Multi-Factor
807 Authentication Delete feature is enabled (True) 912 Authentication Delete feature is enabled (True)
808 or disabled (False). If mfa_delete is enabled 913 or disabled (False). If mfa_delete is enabled
809 then all Delete operations will require the 914 then all Delete operations will require the
810 token from your MFA device to be passed in 915 token from your MFA device to be passed in
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
902 else: 1007 else:
903 raise self.connection.provider.storage_response_error( 1008 raise self.connection.provider.storage_response_error(
904 response.status, response.reason, body) 1009 response.status, response.reason, body)
905 1010
906 def get_website_configuration(self, headers=None): 1011 def get_website_configuration(self, headers=None):
907 """ 1012 """
908 Returns the current status of website configuration on the bucket. 1013 Returns the current status of website configuration on the bucket.
909 1014
910 :rtype: dict 1015 :rtype: dict
911 :returns: A dictionary containing a Python representation 1016 :returns: A dictionary containing a Python representation
912 of the XML response from S3. The overall structure is: 1017 of the XML response from S3. The overall structure is:
913 1018
914 * WebsiteConfiguration 1019 * WebsiteConfiguration
915 * IndexDocument 1020
916 * Suffix : suffix that is appended to request that 1021 * IndexDocument
917 is for a "directory" on the website endpoint 1022
918 * ErrorDocument 1023 * Suffix : suffix that is appended to request that
919 * Key : name of object to serve when an error occurs 1024 is for a "directory" on the website endpoint
1025 * ErrorDocument
1026
1027 * Key : name of object to serve when an error occurs
920 """ 1028 """
921 response = self.connection.make_request('GET', self.name, 1029 response = self.connection.make_request('GET', self.name,
922 query_args='website', headers=headers) 1030 query_args='website', headers=headers)
923 body = response.read() 1031 body = response.read()
924 boto.log.debug(body) 1032 boto.log.debug(body)
925 if response.status == 200: 1033 if response.status == 200:
926 e = boto.jsonresponse.Element() 1034 e = boto.jsonresponse.Element()
927 h = boto.jsonresponse.XmlHandler(e, None) 1035 h = boto.jsonresponse.XmlHandler(e, None)
928 h.parse(body) 1036 h.parse(body)
929 return e 1037 return e
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
971 data=policy, 1079 data=policy,
972 query_args='policy', 1080 query_args='policy',
973 headers=headers) 1081 headers=headers)
974 body = response.read() 1082 body = response.read()
975 if response.status >= 200 and response.status <= 204: 1083 if response.status >= 200 and response.status <= 204:
976 return True 1084 return True
977 else: 1085 else:
978 raise self.connection.provider.storage_response_error( 1086 raise self.connection.provider.storage_response_error(
979 response.status, response.reason, body) 1087 response.status, response.reason, body)
980 1088
981 def initiate_multipart_upload(self, key_name, headers=None): 1089 def delete_policy(self, headers=None):
1090 response = self.connection.make_request('DELETE', self.name,
1091 data='/?policy',
1092 query_args='policy',
1093 headers=headers)
1094 body = response.read()
1095 if response.status >= 200 and response.status <= 204:
1096 return True
1097 else:
1098 raise self.connection.provider.storage_response_error(
1099 response.status, response.reason, body)
1100
1101
1102 def initiate_multipart_upload(self, key_name, headers=None,
1103 reduced_redundancy=False,
1104 metadata=None, encrypt_key=False):
1105 """
1106 Start a multipart upload operation.
1107
1108 :type key_name: string
1109 :param key_name: The name of the key that will ultimately result from
1110 this multipart upload operation. This will be exactly
1111 as the key appears in the bucket after the upload
1112 process has been completed.
1113
1114 :type headers: dict
1115 :param headers: Additional HTTP headers to send and store with the
1116 resulting key in S3.
1117
1118 :type reduced_redundancy: boolean
1119 :param reduced_redundancy: In multipart uploads, the storage class is
1120 specified when initiating the upload,
1121 not when uploading individual parts. So
1122 if you want the resulting key to use the
1123 reduced redundancy storage class set this
1124 flag when you initiate the upload.
1125
1126 :type metadata: dict
1127 :param metadata: Any metadata that you would like to set on the key
1128 that results from the multipart upload.
1129
1130 :type encrypt_key: bool
1131 :param encrypt_key: If True, the new copy of the object will
1132 be encrypted on the server-side by S3 and
1133 will be stored in an encrypted form while
1134 at rest in S3.
1135 """
982 query_args = 'uploads' 1136 query_args = 'uploads'
1137 provider = self.connection.provider
1138 if headers is None:
1139 headers = {}
1140 if reduced_redundancy:
1141 storage_class_header = provider.storage_class_header
1142 if storage_class_header:
1143 headers[storage_class_header] = 'REDUCED_REDUNDANCY'
1144 # TODO: what if the provider doesn't support reduced redundancy?
1145 # (see boto.s3.key.Key.set_contents_from_file)
1146 if encrypt_key:
1147 headers[provider.server_side_encryption_header] = 'AES256'
1148 if metadata is None:
1149 metadata = {}
1150
1151 headers = boto.utils.merge_meta(headers, metadata,
1152 self.connection.provider)
983 response = self.connection.make_request('POST', self.name, key_name, 1153 response = self.connection.make_request('POST', self.name, key_name,
984 query_args=query_args, 1154 query_args=query_args,
985 headers=headers) 1155 headers=headers)
986 body = response.read() 1156 body = response.read()
987 boto.log.debug(body) 1157 boto.log.debug(body)
988 if response.status == 200: 1158 if response.status == 200:
989 resp = MultiPartUpload(self) 1159 resp = MultiPartUpload(self)
990 h = handler.XmlHandler(resp, self) 1160 h = handler.XmlHandler(resp, self)
991 xml.sax.parseString(body, h) 1161 xml.sax.parseString(body, h)
992 return resp 1162 return resp
993 else: 1163 else:
994 raise self.connection.provider.storage_response_error( 1164 raise self.connection.provider.storage_response_error(
995 response.status, response.reason, body) 1165 response.status, response.reason, body)
996 1166
997 def complete_multipart_upload(self, key_name, upload_id, 1167 def complete_multipart_upload(self, key_name, upload_id,
998 xml_body, headers=None): 1168 xml_body, headers=None):
1169 """
1170 Complete a multipart upload operation.
1171 """
999 query_args = 'uploadId=%s' % upload_id 1172 query_args = 'uploadId=%s' % upload_id
1000 if headers is None: 1173 if headers is None:
1001 headers = {} 1174 headers = {}
1002 headers['Content-Type'] = 'text/xml' 1175 headers['Content-Type'] = 'text/xml'
1003 response = self.connection.make_request('POST', self.name, key_name, 1176 response = self.connection.make_request('POST', self.name, key_name,
1004 query_args=query_args, 1177 query_args=query_args,
1005 headers=headers, data=xml_body) 1178 headers=headers, data=xml_body)
1179 contains_error = False
1006 body = response.read() 1180 body = response.read()
1181 # Some errors will be reported in the body of the response
1182 # even though the HTTP response code is 200. This check
1183 # does a quick and dirty peek in the body for an error element.
1184 if body.find('<Error>') > 0:
1185 contains_error = True
1007 boto.log.debug(body) 1186 boto.log.debug(body)
1008 if response.status == 200: 1187 if response.status == 200 and not contains_error:
1009 resp = CompleteMultiPartUpload(self) 1188 resp = CompleteMultiPartUpload(self)
1010 h = handler.XmlHandler(resp, self) 1189 h = handler.XmlHandler(resp, self)
1011 xml.sax.parseString(body, h) 1190 xml.sax.parseString(body, h)
1012 return resp 1191 return resp
1013 else: 1192 else:
1014 raise self.connection.provider.storage_response_error( 1193 raise self.connection.provider.storage_response_error(
1015 response.status, response.reason, body) 1194 response.status, response.reason, body)
1016 1195
1017 def cancel_multipart_upload(self, key_name, upload_id, headers=None): 1196 def cancel_multipart_upload(self, key_name, upload_id, headers=None):
1018 query_args = 'uploadId=%s' % upload_id 1197 query_args = 'uploadId=%s' % upload_id
1019 response = self.connection.make_request('DELETE', self.name, key_name, 1198 response = self.connection.make_request('DELETE', self.name, key_name,
1020 query_args=query_args, 1199 query_args=query_args,
1021 headers=headers) 1200 headers=headers)
1022 body = response.read() 1201 body = response.read()
1023 boto.log.debug(body) 1202 boto.log.debug(body)
1024 if response.status != 204: 1203 if response.status != 204:
1025 raise self.connection.provider.storage_response_error( 1204 raise self.connection.provider.storage_response_error(
1026 response.status, response.reason, body) 1205 response.status, response.reason, body)
1027 1206
1028 def delete(self, headers=None): 1207 def delete(self, headers=None):
1029 return self.connection.delete_bucket(self.name, headers=headers) 1208 return self.connection.delete_bucket(self.name, headers=headers)
1030
OLDNEW
« no previous file with comments | « boto/s3/acl.py ('k') | boto/s3/bucketlistresultset.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698