Mercurial > repos > bcclaywell > argo_navis
comparison venv/lib/python2.7/site-packages/boto/gs/bucket.py @ 0:d67268158946 draft
planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
author | bcclaywell |
---|---|
date | Mon, 12 Oct 2015 17:43:33 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d67268158946 |
---|---|
1 # Copyright 2010 Google Inc. | |
2 # | |
3 # Permission is hereby granted, free of charge, to any person obtaining a | |
4 # copy of this software and associated documentation files (the | |
5 # "Software"), to deal in the Software without restriction, including | |
6 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
7 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
8 # persons to whom the Software is furnished to do so, subject to the fol- | |
9 # lowing conditions: | |
10 # | |
11 # The above copyright notice and this permission notice shall be included | |
12 # in all copies or substantial portions of the Software. | |
13 # | |
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
20 # IN THE SOFTWARE. | |
21 | |
22 import re | |
23 import urllib | |
24 import xml.sax | |
25 | |
26 import boto | |
27 from boto import handler | |
28 from boto.resultset import ResultSet | |
29 from boto.exception import GSResponseError | |
30 from boto.exception import InvalidAclError | |
31 from boto.gs.acl import ACL, CannedACLStrings | |
32 from boto.gs.acl import SupportedPermissions as GSPermissions | |
33 from boto.gs.bucketlistresultset import VersionedBucketListResultSet | |
34 from boto.gs.cors import Cors | |
35 from boto.gs.lifecycle import LifecycleConfig | |
36 from boto.gs.key import Key as GSKey | |
37 from boto.s3.acl import Policy | |
38 from boto.s3.bucket import Bucket as S3Bucket | |
39 from boto.utils import get_utf8_value | |
40 from boto.compat import six | |
41 | |
42 # constants for http query args | |
43 DEF_OBJ_ACL = 'defaultObjectAcl' | |
44 STANDARD_ACL = 'acl' | |
45 CORS_ARG = 'cors' | |
46 LIFECYCLE_ARG = 'lifecycle' | |
47 ERROR_DETAILS_REGEX = re.compile(r'<Details>(?P<details>.*)</Details>') | |
48 | |
49 class Bucket(S3Bucket): | |
50 """Represents a Google Cloud Storage bucket.""" | |
51 | |
52 VersioningBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' | |
53 '<VersioningConfiguration><Status>%s</Status>' | |
54 '</VersioningConfiguration>') | |
55 WebsiteBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' | |
56 '<WebsiteConfiguration>%s%s</WebsiteConfiguration>') | |
57 WebsiteMainPageFragment = '<MainPageSuffix>%s</MainPageSuffix>' | |
58 WebsiteErrorFragment = '<NotFoundPage>%s</NotFoundPage>' | |
59 | |
60 def __init__(self, connection=None, name=None, key_class=GSKey): | |
61 super(Bucket, self).__init__(connection, name, key_class) | |
62 | |
63 def startElement(self, name, attrs, connection): | |
64 return None | |
65 | |
66 def endElement(self, name, value, connection): | |
67 if name == 'Name': | |
68 self.name = value | |
69 elif name == 'CreationDate': | |
70 self.creation_date = value | |
71 else: | |
72 setattr(self, name, value) | |
73 | |
74 def get_key(self, key_name, headers=None, version_id=None, | |
75 response_headers=None, generation=None): | |
76 """Returns a Key instance for an object in this bucket. | |
77 | |
78 Note that this method uses a HEAD request to check for the existence of | |
79 the key. | |
80 | |
81 :type key_name: string | |
82 :param key_name: The name of the key to retrieve | |
83 | |
84 :type response_headers: dict | |
85 :param response_headers: A dictionary containing HTTP | |
86 headers/values that will override any headers associated | |
87 with the stored object in the response. See | |
88 http://goo.gl/06N3b for details. | |
89 | |
90 :type version_id: string | |
91 :param version_id: Unused in this subclass. | |
92 | |
93 :type generation: int | |
94 :param generation: A specific generation number to fetch the key at. If | |
95 not specified, the latest generation is fetched. | |
96 | |
97 :rtype: :class:`boto.gs.key.Key` | |
98 :returns: A Key object from this bucket. | |
99 """ | |
100 query_args_l = [] | |
101 if generation: | |
102 query_args_l.append('generation=%s' % generation) | |
103 if response_headers: | |
104 for rk, rv in six.iteritems(response_headers): | |
105 query_args_l.append('%s=%s' % (rk, urllib.quote(rv))) | |
106 try: | |
107 key, resp = self._get_key_internal(key_name, headers, | |
108 query_args_l=query_args_l) | |
109 except GSResponseError as e: | |
110 if e.status == 403 and 'Forbidden' in e.reason: | |
111 # If we failed getting an object, let the user know which object | |
112 # failed rather than just returning a generic 403. | |
113 e.reason = ("Access denied to 'gs://%s/%s'." % | |
114 (self.name, key_name)) | |
115 raise | |
116 return key | |
117 | |
118 def copy_key(self, new_key_name, src_bucket_name, src_key_name, | |
119 metadata=None, src_version_id=None, storage_class='STANDARD', | |
120 preserve_acl=False, encrypt_key=False, headers=None, | |
121 query_args=None, src_generation=None): | |
122 """Create a new key in the bucket by copying an existing key. | |
123 | |
124 :type new_key_name: string | |
125 :param new_key_name: The name of the new key | |
126 | |
127 :type src_bucket_name: string | |
128 :param src_bucket_name: The name of the source bucket | |
129 | |
130 :type src_key_name: string | |
131 :param src_key_name: The name of the source key | |
132 | |
133 :type src_generation: int | |
134 :param src_generation: The generation number of the source key to copy. | |
135 If not specified, the latest generation is copied. | |
136 | |
137 :type metadata: dict | |
138 :param metadata: Metadata to be associated with new key. If | |
139 metadata is supplied, it will replace the metadata of the | |
140 source key being copied. If no metadata is supplied, the | |
141 source key's metadata will be copied to the new key. | |
142 | |
143 :type version_id: string | |
144 :param version_id: Unused in this subclass. | |
145 | |
146 :type storage_class: string | |
147 :param storage_class: The storage class of the new key. By | |
148 default, the new key will use the standard storage class. | |
149 Possible values are: STANDARD | DURABLE_REDUCED_AVAILABILITY | |
150 | |
151 :type preserve_acl: bool | |
152 :param preserve_acl: If True, the ACL from the source key will | |
153 be copied to the destination key. If False, the | |
154 destination key will have the default ACL. Note that | |
155 preserving the ACL in the new key object will require two | |
156 additional API calls to GCS, one to retrieve the current | |
157 ACL and one to set that ACL on the new object. If you | |
158 don't care about the ACL (or if you have a default ACL set | |
159 on the bucket), a value of False will be significantly more | |
160 efficient. | |
161 | |
162 :type encrypt_key: bool | |
163 :param encrypt_key: Included for compatibility with S3. This argument is | |
164 ignored. | |
165 | |
166 :type headers: dict | |
167 :param headers: A dictionary of header name/value pairs. | |
168 | |
169 :type query_args: string | |
170 :param query_args: A string of additional querystring arguments | |
171 to append to the request | |
172 | |
173 :rtype: :class:`boto.gs.key.Key` | |
174 :returns: An instance of the newly created key object | |
175 """ | |
176 if src_generation: | |
177 headers = headers or {} | |
178 headers['x-goog-copy-source-generation'] = str(src_generation) | |
179 return super(Bucket, self).copy_key( | |
180 new_key_name, src_bucket_name, src_key_name, metadata=metadata, | |
181 storage_class=storage_class, preserve_acl=preserve_acl, | |
182 encrypt_key=encrypt_key, headers=headers, query_args=query_args) | |
183 | |
184 def list_versions(self, prefix='', delimiter='', marker='', | |
185 generation_marker='', headers=None): | |
186 """ | |
187 List versioned objects within a bucket. This returns an | |
188 instance of an VersionedBucketListResultSet that automatically | |
189 handles all of the result paging, etc. from GCS. You just need | |
190 to keep iterating until there are no more results. Called | |
191 with no arguments, this will return an iterator object across | |
192 all keys within the bucket. | |
193 | |
194 :type prefix: string | |
195 :param prefix: allows you to limit the listing to a particular | |
196 prefix. For example, if you call the method with | |
197 prefix='/foo/' then the iterator will only cycle through | |
198 the keys that begin with the string '/foo/'. | |
199 | |
200 :type delimiter: string | |
201 :param delimiter: can be used in conjunction with the prefix | |
202 to allow you to organize and browse your keys | |
203 hierarchically. See: | |
204 https://developers.google.com/storage/docs/reference-headers#delimiter | |
205 for more details. | |
206 | |
207 :type marker: string | |
208 :param marker: The "marker" of where you are in the result set | |
209 | |
210 :type generation_marker: string | |
211 :param generation_marker: The "generation marker" of where you are in | |
212 the result set. | |
213 | |
214 :type headers: dict | |
215 :param headers: A dictionary of header name/value pairs. | |
216 | |
217 :rtype: | |
218 :class:`boto.gs.bucketlistresultset.VersionedBucketListResultSet` | |
219 :return: an instance of a BucketListResultSet that handles paging, etc. | |
220 """ | |
221 return VersionedBucketListResultSet(self, prefix, delimiter, | |
222 marker, generation_marker, | |
223 headers) | |
224 | |
225 def validate_get_all_versions_params(self, params): | |
226 """ | |
227 See documentation in boto/s3/bucket.py. | |
228 """ | |
229 self.validate_kwarg_names(params, | |
230 ['version_id_marker', 'delimiter', 'marker', | |
231 'generation_marker', 'prefix', 'max_keys']) | |
232 | |
233 def delete_key(self, key_name, headers=None, version_id=None, | |
234 mfa_token=None, generation=None): | |
235 """ | |
236 Deletes a key from the bucket. | |
237 | |
238 :type key_name: string | |
239 :param key_name: The key name to delete | |
240 | |
241 :type headers: dict | |
242 :param headers: A dictionary of header name/value pairs. | |
243 | |
244 :type version_id: string | |
245 :param version_id: Unused in this subclass. | |
246 | |
247 :type mfa_token: tuple or list of strings | |
248 :param mfa_token: Unused in this subclass. | |
249 | |
250 :type generation: int | |
251 :param generation: The generation number of the key to delete. If not | |
252 specified, the latest generation number will be deleted. | |
253 | |
254 :rtype: :class:`boto.gs.key.Key` | |
255 :returns: A key object holding information on what was | |
256 deleted. | |
257 """ | |
258 query_args_l = [] | |
259 if generation: | |
260 query_args_l.append('generation=%s' % generation) | |
261 self._delete_key_internal(key_name, headers=headers, | |
262 version_id=version_id, mfa_token=mfa_token, | |
263 query_args_l=query_args_l) | |
264 | |
265 def set_acl(self, acl_or_str, key_name='', headers=None, version_id=None, | |
266 generation=None, if_generation=None, if_metageneration=None): | |
267 """Sets or changes a bucket's or key's ACL. | |
268 | |
269 :type acl_or_str: string or :class:`boto.gs.acl.ACL` | |
270 :param acl_or_str: A canned ACL string (see | |
271 :data:`~.gs.acl.CannedACLStrings`) or an ACL object. | |
272 | |
273 :type key_name: string | |
274 :param key_name: A key name within the bucket to set the ACL for. If not | |
275 specified, the ACL for the bucket will be set. | |
276 | |
277 :type headers: dict | |
278 :param headers: Additional headers to set during the request. | |
279 | |
280 :type version_id: string | |
281 :param version_id: Unused in this subclass. | |
282 | |
283 :type generation: int | |
284 :param generation: If specified, sets the ACL for a specific generation | |
285 of a versioned object. If not specified, the current version is | |
286 modified. | |
287 | |
288 :type if_generation: int | |
289 :param if_generation: (optional) If set to a generation number, the acl | |
290 will only be updated if its current generation number is this value. | |
291 | |
292 :type if_metageneration: int | |
293 :param if_metageneration: (optional) If set to a metageneration number, | |
294 the acl will only be updated if its current metageneration number is | |
295 this value. | |
296 """ | |
297 if isinstance(acl_or_str, Policy): | |
298 raise InvalidAclError('Attempt to set S3 Policy on GS ACL') | |
299 elif isinstance(acl_or_str, ACL): | |
300 self.set_xml_acl(acl_or_str.to_xml(), key_name, headers=headers, | |
301 generation=generation, | |
302 if_generation=if_generation, | |
303 if_metageneration=if_metageneration) | |
304 else: | |
305 self.set_canned_acl(acl_or_str, key_name, headers=headers, | |
306 generation=generation, | |
307 if_generation=if_generation, | |
308 if_metageneration=if_metageneration) | |
309 | |
310 def set_def_acl(self, acl_or_str, headers=None): | |
311 """Sets or changes a bucket's default ACL. | |
312 | |
313 :type acl_or_str: string or :class:`boto.gs.acl.ACL` | |
314 :param acl_or_str: A canned ACL string (see | |
315 :data:`~.gs.acl.CannedACLStrings`) or an ACL object. | |
316 | |
317 :type headers: dict | |
318 :param headers: Additional headers to set during the request. | |
319 """ | |
320 if isinstance(acl_or_str, Policy): | |
321 raise InvalidAclError('Attempt to set S3 Policy on GS ACL') | |
322 elif isinstance(acl_or_str, ACL): | |
323 self.set_def_xml_acl(acl_or_str.to_xml(), headers=headers) | |
324 else: | |
325 self.set_def_canned_acl(acl_or_str, headers=headers) | |
326 | |
327 def _get_xml_acl_helper(self, key_name, headers, query_args): | |
328 """Provides common functionality for get_xml_acl and _get_acl_helper.""" | |
329 response = self.connection.make_request('GET', self.name, key_name, | |
330 query_args=query_args, | |
331 headers=headers) | |
332 body = response.read() | |
333 if response.status != 200: | |
334 if response.status == 403: | |
335 match = ERROR_DETAILS_REGEX.search(body) | |
336 details = match.group('details') if match else None | |
337 if details: | |
338 details = (('<Details>%s. Note that Full Control access' | |
339 ' is required to access ACLs.</Details>') % | |
340 details) | |
341 body = re.sub(ERROR_DETAILS_REGEX, details, body) | |
342 raise self.connection.provider.storage_response_error( | |
343 response.status, response.reason, body) | |
344 return body | |
345 | |
346 def _get_acl_helper(self, key_name, headers, query_args): | |
347 """Provides common functionality for get_acl and get_def_acl.""" | |
348 body = self._get_xml_acl_helper(key_name, headers, query_args) | |
349 acl = ACL(self) | |
350 h = handler.XmlHandler(acl, self) | |
351 xml.sax.parseString(body, h) | |
352 return acl | |
353 | |
354 def get_acl(self, key_name='', headers=None, version_id=None, | |
355 generation=None): | |
356 """Returns the ACL of the bucket or an object in the bucket. | |
357 | |
358 :param str key_name: The name of the object to get the ACL for. If not | |
359 specified, the ACL for the bucket will be returned. | |
360 | |
361 :param dict headers: Additional headers to set during the request. | |
362 | |
363 :type version_id: string | |
364 :param version_id: Unused in this subclass. | |
365 | |
366 :param int generation: If specified, gets the ACL for a specific | |
367 generation of a versioned object. If not specified, the current | |
368 version is returned. This parameter is only valid when retrieving | |
369 the ACL of an object, not a bucket. | |
370 | |
371 :rtype: :class:`.gs.acl.ACL` | |
372 """ | |
373 query_args = STANDARD_ACL | |
374 if generation: | |
375 query_args += '&generation=%s' % generation | |
376 return self._get_acl_helper(key_name, headers, query_args) | |
377 | |
378 def get_xml_acl(self, key_name='', headers=None, version_id=None, | |
379 generation=None): | |
380 """Returns the ACL string of the bucket or an object in the bucket. | |
381 | |
382 :param str key_name: The name of the object to get the ACL for. If not | |
383 specified, the ACL for the bucket will be returned. | |
384 | |
385 :param dict headers: Additional headers to set during the request. | |
386 | |
387 :type version_id: string | |
388 :param version_id: Unused in this subclass. | |
389 | |
390 :param int generation: If specified, gets the ACL for a specific | |
391 generation of a versioned object. If not specified, the current | |
392 version is returned. This parameter is only valid when retrieving | |
393 the ACL of an object, not a bucket. | |
394 | |
395 :rtype: str | |
396 """ | |
397 query_args = STANDARD_ACL | |
398 if generation: | |
399 query_args += '&generation=%s' % generation | |
400 return self._get_xml_acl_helper(key_name, headers, query_args) | |
401 | |
402 def get_def_acl(self, headers=None): | |
403 """Returns the bucket's default ACL. | |
404 | |
405 :param dict headers: Additional headers to set during the request. | |
406 | |
407 :rtype: :class:`.gs.acl.ACL` | |
408 """ | |
409 return self._get_acl_helper('', headers, DEF_OBJ_ACL) | |
410 | |
411 def _set_acl_helper(self, acl_or_str, key_name, headers, query_args, | |
412 generation, if_generation, if_metageneration, | |
413 canned=False): | |
414 """Provides common functionality for set_acl, set_xml_acl, | |
415 set_canned_acl, set_def_acl, set_def_xml_acl, and | |
416 set_def_canned_acl().""" | |
417 | |
418 headers = headers or {} | |
419 data = '' | |
420 if canned: | |
421 headers[self.connection.provider.acl_header] = acl_or_str | |
422 else: | |
423 data = acl_or_str | |
424 | |
425 if generation: | |
426 query_args += '&generation=%s' % generation | |
427 | |
428 if if_metageneration is not None and if_generation is None: | |
429 raise ValueError("Received if_metageneration argument with no " | |
430 "if_generation argument. A metageneration has no " | |
431 "meaning without a content generation.") | |
432 if not key_name and (if_generation or if_metageneration): | |
433 raise ValueError("Received if_generation or if_metageneration " | |
434 "parameter while setting the ACL of a bucket.") | |
435 if if_generation is not None: | |
436 headers['x-goog-if-generation-match'] = str(if_generation) | |
437 if if_metageneration is not None: | |
438 headers['x-goog-if-metageneration-match'] = str(if_metageneration) | |
439 | |
440 response = self.connection.make_request( | |
441 'PUT', get_utf8_value(self.name), get_utf8_value(key_name), | |
442 data=get_utf8_value(data), headers=headers, query_args=query_args) | |
443 body = response.read() | |
444 if response.status != 200: | |
445 raise self.connection.provider.storage_response_error( | |
446 response.status, response.reason, body) | |
447 | |
448 def set_xml_acl(self, acl_str, key_name='', headers=None, version_id=None, | |
449 query_args='acl', generation=None, if_generation=None, | |
450 if_metageneration=None): | |
451 """Sets a bucket's or objects's ACL to an XML string. | |
452 | |
453 :type acl_str: string | |
454 :param acl_str: A string containing the ACL XML. | |
455 | |
456 :type key_name: string | |
457 :param key_name: A key name within the bucket to set the ACL for. If not | |
458 specified, the ACL for the bucket will be set. | |
459 | |
460 :type headers: dict | |
461 :param headers: Additional headers to set during the request. | |
462 | |
463 :type version_id: string | |
464 :param version_id: Unused in this subclass. | |
465 | |
466 :type query_args: str | |
467 :param query_args: The query parameters to pass with the request. | |
468 | |
469 :type generation: int | |
470 :param generation: If specified, sets the ACL for a specific generation | |
471 of a versioned object. If not specified, the current version is | |
472 modified. | |
473 | |
474 :type if_generation: int | |
475 :param if_generation: (optional) If set to a generation number, the acl | |
476 will only be updated if its current generation number is this value. | |
477 | |
478 :type if_metageneration: int | |
479 :param if_metageneration: (optional) If set to a metageneration number, | |
480 the acl will only be updated if its current metageneration number is | |
481 this value. | |
482 """ | |
483 return self._set_acl_helper(acl_str, key_name=key_name, headers=headers, | |
484 query_args=query_args, | |
485 generation=generation, | |
486 if_generation=if_generation, | |
487 if_metageneration=if_metageneration) | |
488 | |
489 def set_canned_acl(self, acl_str, key_name='', headers=None, | |
490 version_id=None, generation=None, if_generation=None, | |
491 if_metageneration=None): | |
492 """Sets a bucket's or objects's ACL using a predefined (canned) value. | |
493 | |
494 :type acl_str: string | |
495 :param acl_str: A canned ACL string. See | |
496 :data:`~.gs.acl.CannedACLStrings`. | |
497 | |
498 :type key_name: string | |
499 :param key_name: A key name within the bucket to set the ACL for. If not | |
500 specified, the ACL for the bucket will be set. | |
501 | |
502 :type headers: dict | |
503 :param headers: Additional headers to set during the request. | |
504 | |
505 :type version_id: string | |
506 :param version_id: Unused in this subclass. | |
507 | |
508 :type generation: int | |
509 :param generation: If specified, sets the ACL for a specific generation | |
510 of a versioned object. If not specified, the current version is | |
511 modified. | |
512 | |
513 :type if_generation: int | |
514 :param if_generation: (optional) If set to a generation number, the acl | |
515 will only be updated if its current generation number is this value. | |
516 | |
517 :type if_metageneration: int | |
518 :param if_metageneration: (optional) If set to a metageneration number, | |
519 the acl will only be updated if its current metageneration number is | |
520 this value. | |
521 """ | |
522 if acl_str not in CannedACLStrings: | |
523 raise ValueError("Provided canned ACL string (%s) is not valid." | |
524 % acl_str) | |
525 query_args = STANDARD_ACL | |
526 return self._set_acl_helper(acl_str, key_name, headers, query_args, | |
527 generation, if_generation, | |
528 if_metageneration, canned=True) | |
529 | |
530 def set_def_canned_acl(self, acl_str, headers=None): | |
531 """Sets a bucket's default ACL using a predefined (canned) value. | |
532 | |
533 :type acl_str: string | |
534 :param acl_str: A canned ACL string. See | |
535 :data:`~.gs.acl.CannedACLStrings`. | |
536 | |
537 :type headers: dict | |
538 :param headers: Additional headers to set during the request. | |
539 """ | |
540 if acl_str not in CannedACLStrings: | |
541 raise ValueError("Provided canned ACL string (%s) is not valid." | |
542 % acl_str) | |
543 query_args = DEF_OBJ_ACL | |
544 return self._set_acl_helper(acl_str, '', headers, query_args, | |
545 generation=None, if_generation=None, | |
546 if_metageneration=None, canned=True) | |
547 | |
548 def set_def_xml_acl(self, acl_str, headers=None): | |
549 """Sets a bucket's default ACL to an XML string. | |
550 | |
551 :type acl_str: string | |
552 :param acl_str: A string containing the ACL XML. | |
553 | |
554 :type headers: dict | |
555 :param headers: Additional headers to set during the request. | |
556 """ | |
557 return self.set_xml_acl(acl_str, '', headers, | |
558 query_args=DEF_OBJ_ACL) | |
559 | |
560 def get_cors(self, headers=None): | |
561 """Returns a bucket's CORS XML document. | |
562 | |
563 :param dict headers: Additional headers to send with the request. | |
564 :rtype: :class:`~.cors.Cors` | |
565 """ | |
566 response = self.connection.make_request('GET', self.name, | |
567 query_args=CORS_ARG, | |
568 headers=headers) | |
569 body = response.read() | |
570 if response.status == 200: | |
571 # Success - parse XML and return Cors object. | |
572 cors = Cors() | |
573 h = handler.XmlHandler(cors, self) | |
574 xml.sax.parseString(body, h) | |
575 return cors | |
576 else: | |
577 raise self.connection.provider.storage_response_error( | |
578 response.status, response.reason, body) | |
579 | |
580 def set_cors(self, cors, headers=None): | |
581 """Sets a bucket's CORS XML document. | |
582 | |
583 :param str cors: A string containing the CORS XML. | |
584 :param dict headers: Additional headers to send with the request. | |
585 """ | |
586 response = self.connection.make_request( | |
587 'PUT', get_utf8_value(self.name), data=get_utf8_value(cors), | |
588 query_args=CORS_ARG, headers=headers) | |
589 body = response.read() | |
590 if response.status != 200: | |
591 raise self.connection.provider.storage_response_error( | |
592 response.status, response.reason, body) | |
593 | |
594 def get_storage_class(self): | |
595 """ | |
596 Returns the StorageClass for the bucket. | |
597 | |
598 :rtype: str | |
599 :return: The StorageClass for the bucket. | |
600 """ | |
601 response = self.connection.make_request('GET', self.name, | |
602 query_args='storageClass') | |
603 body = response.read() | |
604 if response.status == 200: | |
605 rs = ResultSet(self) | |
606 h = handler.XmlHandler(rs, self) | |
607 xml.sax.parseString(body, h) | |
608 return rs.StorageClass | |
609 else: | |
610 raise self.connection.provider.storage_response_error( | |
611 response.status, response.reason, body) | |
612 | |
613 | |
614 # Method with same signature as boto.s3.bucket.Bucket.add_email_grant(), | |
615 # to allow polymorphic treatment at application layer. | |
616 def add_email_grant(self, permission, email_address, | |
617 recursive=False, headers=None): | |
618 """ | |
619 Convenience method that provides a quick way to add an email grant | |
620 to a bucket. This method retrieves the current ACL, creates a new | |
621 grant based on the parameters passed in, adds that grant to the ACL | |
622 and then PUT's the new ACL back to GCS. | |
623 | |
624 :type permission: string | |
625 :param permission: The permission being granted. Should be one of: | |
626 (READ, WRITE, FULL_CONTROL). | |
627 | |
628 :type email_address: string | |
629 :param email_address: The email address associated with the GS | |
630 account your are granting the permission to. | |
631 | |
632 :type recursive: bool | |
633 :param recursive: A boolean value to controls whether the call | |
634 will apply the grant to all keys within the bucket | |
635 or not. The default value is False. By passing a | |
636 True value, the call will iterate through all keys | |
637 in the bucket and apply the same grant to each key. | |
638 CAUTION: If you have a lot of keys, this could take | |
639 a long time! | |
640 """ | |
641 if permission not in GSPermissions: | |
642 raise self.connection.provider.storage_permissions_error( | |
643 'Unknown Permission: %s' % permission) | |
644 acl = self.get_acl(headers=headers) | |
645 acl.add_email_grant(permission, email_address) | |
646 self.set_acl(acl, headers=headers) | |
647 if recursive: | |
648 for key in self: | |
649 key.add_email_grant(permission, email_address, headers=headers) | |
650 | |
651 # Method with same signature as boto.s3.bucket.Bucket.add_user_grant(), | |
652 # to allow polymorphic treatment at application layer. | |
653 def add_user_grant(self, permission, user_id, recursive=False, | |
654 headers=None): | |
655 """ | |
656 Convenience method that provides a quick way to add a canonical user | |
657 grant to a bucket. This method retrieves the current ACL, creates a new | |
658 grant based on the parameters passed in, adds that grant to the ACL and | |
659 then PUTs the new ACL back to GCS. | |
660 | |
661 :type permission: string | |
662 :param permission: The permission being granted. Should be one of: | |
663 (READ|WRITE|FULL_CONTROL) | |
664 | |
665 :type user_id: string | |
666 :param user_id: The canonical user id associated with the GS account | |
667 you are granting the permission to. | |
668 | |
669 :type recursive: bool | |
670 :param recursive: A boolean value to controls whether the call | |
671 will apply the grant to all keys within the bucket | |
672 or not. The default value is False. By passing a | |
673 True value, the call will iterate through all keys | |
674 in the bucket and apply the same grant to each key. | |
675 CAUTION: If you have a lot of keys, this could take | |
676 a long time! | |
677 """ | |
678 if permission not in GSPermissions: | |
679 raise self.connection.provider.storage_permissions_error( | |
680 'Unknown Permission: %s' % permission) | |
681 acl = self.get_acl(headers=headers) | |
682 acl.add_user_grant(permission, user_id) | |
683 self.set_acl(acl, headers=headers) | |
684 if recursive: | |
685 for key in self: | |
686 key.add_user_grant(permission, user_id, headers=headers) | |
687 | |
688 def add_group_email_grant(self, permission, email_address, recursive=False, | |
689 headers=None): | |
690 """ | |
691 Convenience method that provides a quick way to add an email group | |
692 grant to a bucket. This method retrieves the current ACL, creates a new | |
693 grant based on the parameters passed in, adds that grant to the ACL and | |
694 then PUT's the new ACL back to GCS. | |
695 | |
696 :type permission: string | |
697 :param permission: The permission being granted. Should be one of: | |
698 READ|WRITE|FULL_CONTROL | |
699 See http://code.google.com/apis/storage/docs/developer-guide.html#authorization | |
700 for more details on permissions. | |
701 | |
702 :type email_address: string | |
703 :param email_address: The email address associated with the Google | |
704 Group to which you are granting the permission. | |
705 | |
706 :type recursive: bool | |
707 :param recursive: A boolean value to controls whether the call | |
708 will apply the grant to all keys within the bucket | |
709 or not. The default value is False. By passing a | |
710 True value, the call will iterate through all keys | |
711 in the bucket and apply the same grant to each key. | |
712 CAUTION: If you have a lot of keys, this could take | |
713 a long time! | |
714 """ | |
715 if permission not in GSPermissions: | |
716 raise self.connection.provider.storage_permissions_error( | |
717 'Unknown Permission: %s' % permission) | |
718 acl = self.get_acl(headers=headers) | |
719 acl.add_group_email_grant(permission, email_address) | |
720 self.set_acl(acl, headers=headers) | |
721 if recursive: | |
722 for key in self: | |
723 key.add_group_email_grant(permission, email_address, | |
724 headers=headers) | |
725 | |
726 # Method with same input signature as boto.s3.bucket.Bucket.list_grants() | |
727 # (but returning different object type), to allow polymorphic treatment | |
728 # at application layer. | |
729 def list_grants(self, headers=None): | |
730 """Returns the ACL entries applied to this bucket. | |
731 | |
732 :param dict headers: Additional headers to send with the request. | |
733 :rtype: list containing :class:`~.gs.acl.Entry` objects. | |
734 """ | |
735 acl = self.get_acl(headers=headers) | |
736 return acl.entries | |
737 | |
738 def disable_logging(self, headers=None): | |
739 """Disable logging on this bucket. | |
740 | |
741 :param dict headers: Additional headers to send with the request. | |
742 """ | |
743 xml_str = '<?xml version="1.0" encoding="UTF-8"?><Logging/>' | |
744 self.set_subresource('logging', xml_str, headers=headers) | |
745 | |
746 def enable_logging(self, target_bucket, target_prefix=None, headers=None): | |
747 """Enable logging on a bucket. | |
748 | |
749 :type target_bucket: bucket or string | |
750 :param target_bucket: The bucket to log to. | |
751 | |
752 :type target_prefix: string | |
753 :param target_prefix: The prefix which should be prepended to the | |
754 generated log files written to the target_bucket. | |
755 | |
756 :param dict headers: Additional headers to send with the request. | |
757 """ | |
758 if isinstance(target_bucket, Bucket): | |
759 target_bucket = target_bucket.name | |
760 xml_str = '<?xml version="1.0" encoding="UTF-8"?><Logging>' | |
761 xml_str = (xml_str + '<LogBucket>%s</LogBucket>' % target_bucket) | |
762 if target_prefix: | |
763 xml_str = (xml_str + | |
764 '<LogObjectPrefix>%s</LogObjectPrefix>' % target_prefix) | |
765 xml_str = xml_str + '</Logging>' | |
766 | |
767 self.set_subresource('logging', xml_str, headers=headers) | |
768 | |
769 def get_logging_config_with_xml(self, headers=None): | |
770 """Returns the current status of logging configuration on the bucket as | |
771 unparsed XML. | |
772 | |
773 :param dict headers: Additional headers to send with the request. | |
774 | |
775 :rtype: 2-Tuple | |
776 :returns: 2-tuple containing: | |
777 | |
778 1) A dictionary containing the parsed XML response from GCS. The | |
779 overall structure is: | |
780 | |
781 * Logging | |
782 | |
783 * LogObjectPrefix: Prefix that is prepended to log objects. | |
784 * LogBucket: Target bucket for log objects. | |
785 | |
786 2) Unparsed XML describing the bucket's logging configuration. | |
787 """ | |
788 response = self.connection.make_request('GET', self.name, | |
789 query_args='logging', | |
790 headers=headers) | |
791 body = response.read() | |
792 boto.log.debug(body) | |
793 | |
794 if response.status != 200: | |
795 raise self.connection.provider.storage_response_error( | |
796 response.status, response.reason, body) | |
797 | |
798 e = boto.jsonresponse.Element() | |
799 h = boto.jsonresponse.XmlHandler(e, None) | |
800 h.parse(body) | |
801 return e, body | |
802 | |
803 def get_logging_config(self, headers=None): | |
804 """Returns the current status of logging configuration on the bucket. | |
805 | |
806 :param dict headers: Additional headers to send with the request. | |
807 | |
808 :rtype: dict | |
809 :returns: A dictionary containing the parsed XML response from GCS. The | |
810 overall structure is: | |
811 | |
812 * Logging | |
813 | |
814 * LogObjectPrefix: Prefix that is prepended to log objects. | |
815 * LogBucket: Target bucket for log objects. | |
816 """ | |
817 return self.get_logging_config_with_xml(headers)[0] | |
818 | |
819 def configure_website(self, main_page_suffix=None, error_key=None, | |
820 headers=None): | |
821 """Configure this bucket to act as a website | |
822 | |
823 :type main_page_suffix: str | |
824 :param main_page_suffix: Suffix that is appended to a request that is | |
825 for a "directory" on the website endpoint (e.g. if the suffix is | |
826 index.html and you make a request to samplebucket/images/ the data | |
827 that is returned will be for the object with the key name | |
828 images/index.html). The suffix must not be empty and must not | |
829 include a slash character. This parameter is optional and the | |
830 property is disabled if excluded. | |
831 | |
832 :type error_key: str | |
833 :param error_key: The object key name to use when a 400 error occurs. | |
834 This parameter is optional and the property is disabled if excluded. | |
835 | |
836 :param dict headers: Additional headers to send with the request. | |
837 """ | |
838 if main_page_suffix: | |
839 main_page_frag = self.WebsiteMainPageFragment % main_page_suffix | |
840 else: | |
841 main_page_frag = '' | |
842 | |
843 if error_key: | |
844 error_frag = self.WebsiteErrorFragment % error_key | |
845 else: | |
846 error_frag = '' | |
847 | |
848 body = self.WebsiteBody % (main_page_frag, error_frag) | |
849 response = self.connection.make_request( | |
850 'PUT', get_utf8_value(self.name), data=get_utf8_value(body), | |
851 query_args='websiteConfig', headers=headers) | |
852 body = response.read() | |
853 if response.status == 200: | |
854 return True | |
855 else: | |
856 raise self.connection.provider.storage_response_error( | |
857 response.status, response.reason, body) | |
858 | |
859 def get_website_configuration(self, headers=None): | |
860 """Returns the current status of website configuration on the bucket. | |
861 | |
862 :param dict headers: Additional headers to send with the request. | |
863 | |
864 :rtype: dict | |
865 :returns: A dictionary containing the parsed XML response from GCS. The | |
866 overall structure is: | |
867 | |
868 * WebsiteConfiguration | |
869 | |
870 * MainPageSuffix: suffix that is appended to request that | |
871 is for a "directory" on the website endpoint. | |
872 * NotFoundPage: name of an object to serve when site visitors | |
873 encounter a 404. | |
874 """ | |
875 return self.get_website_configuration_with_xml(headers)[0] | |
876 | |
877 def get_website_configuration_with_xml(self, headers=None): | |
878 """Returns the current status of website configuration on the bucket as | |
879 unparsed XML. | |
880 | |
881 :param dict headers: Additional headers to send with the request. | |
882 | |
883 :rtype: 2-Tuple | |
884 :returns: 2-tuple containing: | |
885 | |
886 1) A dictionary containing the parsed XML response from GCS. The | |
887 overall structure is: | |
888 | |
889 * WebsiteConfiguration | |
890 | |
891 * MainPageSuffix: suffix that is appended to request that is for | |
892 a "directory" on the website endpoint. | |
893 * NotFoundPage: name of an object to serve when site visitors | |
894 encounter a 404 | |
895 | |
896 2) Unparsed XML describing the bucket's website configuration. | |
897 """ | |
898 response = self.connection.make_request('GET', self.name, | |
899 query_args='websiteConfig', headers=headers) | |
900 body = response.read() | |
901 boto.log.debug(body) | |
902 | |
903 if response.status != 200: | |
904 raise self.connection.provider.storage_response_error( | |
905 response.status, response.reason, body) | |
906 | |
907 e = boto.jsonresponse.Element() | |
908 h = boto.jsonresponse.XmlHandler(e, None) | |
909 h.parse(body) | |
910 return e, body | |
911 | |
912 def delete_website_configuration(self, headers=None): | |
913 """Remove the website configuration from this bucket. | |
914 | |
915 :param dict headers: Additional headers to send with the request. | |
916 """ | |
917 self.configure_website(headers=headers) | |
918 | |
919 def get_versioning_status(self, headers=None): | |
920 """Returns the current status of versioning configuration on the bucket. | |
921 | |
922 :rtype: bool | |
923 """ | |
924 response = self.connection.make_request('GET', self.name, | |
925 query_args='versioning', | |
926 headers=headers) | |
927 body = response.read() | |
928 boto.log.debug(body) | |
929 if response.status != 200: | |
930 raise self.connection.provider.storage_response_error( | |
931 response.status, response.reason, body) | |
932 resp_json = boto.jsonresponse.Element() | |
933 boto.jsonresponse.XmlHandler(resp_json, None).parse(body) | |
934 resp_json = resp_json['VersioningConfiguration'] | |
935 return ('Status' in resp_json) and (resp_json['Status'] == 'Enabled') | |
936 | |
937 def configure_versioning(self, enabled, headers=None): | |
938 """Configure versioning for this bucket. | |
939 | |
940 :param bool enabled: If set to True, enables versioning on this bucket. | |
941 If set to False, disables versioning. | |
942 | |
943 :param dict headers: Additional headers to send with the request. | |
944 """ | |
945 if enabled == True: | |
946 req_body = self.VersioningBody % ('Enabled') | |
947 else: | |
948 req_body = self.VersioningBody % ('Suspended') | |
949 self.set_subresource('versioning', req_body, headers=headers) | |
950 | |
951 def get_lifecycle_config(self, headers=None): | |
952 """ | |
953 Returns the current lifecycle configuration on the bucket. | |
954 | |
955 :rtype: :class:`boto.gs.lifecycle.LifecycleConfig` | |
956 :returns: A LifecycleConfig object that describes all current | |
957 lifecycle rules in effect for the bucket. | |
958 """ | |
959 response = self.connection.make_request('GET', self.name, | |
960 query_args=LIFECYCLE_ARG, headers=headers) | |
961 body = response.read() | |
962 boto.log.debug(body) | |
963 if response.status == 200: | |
964 lifecycle_config = LifecycleConfig() | |
965 h = handler.XmlHandler(lifecycle_config, self) | |
966 xml.sax.parseString(body, h) | |
967 return lifecycle_config | |
968 else: | |
969 raise self.connection.provider.storage_response_error( | |
970 response.status, response.reason, body) | |
971 | |
972 def configure_lifecycle(self, lifecycle_config, headers=None): | |
973 """ | |
974 Configure lifecycle for this bucket. | |
975 | |
976 :type lifecycle_config: :class:`boto.gs.lifecycle.LifecycleConfig` | |
977 :param lifecycle_config: The lifecycle configuration you want | |
978 to configure for this bucket. | |
979 """ | |
980 xml = lifecycle_config.to_xml() | |
981 response = self.connection.make_request( | |
982 'PUT', get_utf8_value(self.name), data=get_utf8_value(xml), | |
983 query_args=LIFECYCLE_ARG, headers=headers) | |
984 body = response.read() | |
985 if response.status == 200: | |
986 return True | |
987 else: | |
988 raise self.connection.provider.storage_response_error( | |
989 response.status, response.reason, body) |