| OLD | NEW |
| (Empty) | |
| 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 boto |
| 23 from boto import handler |
| 24 from boto.resultset import ResultSet |
| 25 from boto.exception import InvalidAclError |
| 26 from boto.gs.acl import ACL, CannedACLStrings |
| 27 from boto.gs.acl import SupportedPermissions as GSPermissions |
| 28 from boto.gs.bucketlistresultset import VersionedBucketListResultSet |
| 29 from boto.gs.cors import Cors |
| 30 from boto.gs.key import Key as GSKey |
| 31 from boto.s3.acl import Policy |
| 32 from boto.s3.bucket import Bucket as S3Bucket |
| 33 import xml.sax |
| 34 |
| 35 # constants for http query args |
| 36 DEF_OBJ_ACL = 'defaultObjectAcl' |
| 37 STANDARD_ACL = 'acl' |
| 38 CORS_ARG = 'cors' |
| 39 |
| 40 class Bucket(S3Bucket): |
| 41 VersioningBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' |
| 42 '<VersioningConfiguration><Status>%s</Status>' |
| 43 '</VersioningConfiguration>') |
| 44 WebsiteBody = ('<?xml version="1.0" encoding="UTF-8"?>\n' |
| 45 '<WebsiteConfiguration>%s%s</WebsiteConfiguration>') |
| 46 WebsiteMainPageFragment = '<MainPageSuffix>%s</MainPageSuffix>' |
| 47 WebsiteErrorFragment = '<NotFoundPage>%s</NotFoundPage>' |
| 48 |
| 49 def __init__(self, connection=None, name=None, key_class=GSKey): |
| 50 super(Bucket, self).__init__(connection, name, key_class) |
| 51 |
| 52 def startElement(self, name, attrs, connection): |
| 53 return None |
| 54 |
| 55 def endElement(self, name, value, connection): |
| 56 if name == 'Name': |
| 57 self.name = value |
| 58 elif name == 'CreationDate': |
| 59 self.creation_date = value |
| 60 else: |
| 61 setattr(self, name, value) |
| 62 |
| 63 def get_key(self, key_name, headers=None, version_id=None, |
| 64 response_headers=None, generation=None): |
| 65 """ |
| 66 Check to see if a particular key exists within the bucket. This |
| 67 method uses a HEAD request to check for the existance of the key. |
| 68 Returns: An instance of a Key object or None |
| 69 |
| 70 :type key_name: string |
| 71 :param key_name: The name of the key to retrieve |
| 72 |
| 73 :type response_headers: dict |
| 74 :param response_headers: A dictionary containing HTTP |
| 75 headers/values that will override any headers associated |
| 76 with the stored object in the response. See |
| 77 http://goo.gl/06N3b for details. |
| 78 |
| 79 :rtype: :class:`boto.s3.key.Key` |
| 80 :returns: A Key object from this bucket. |
| 81 """ |
| 82 query_args_l = [] |
| 83 if generation: |
| 84 query_args_l.append('generation=%s' % generation) |
| 85 if response_headers: |
| 86 for rk, rv in response_headers.iteritems(): |
| 87 query_args_l.append('%s=%s' % (rk, urllib.quote(rv))) |
| 88 |
| 89 key, resp = self._get_key_internal(key_name, headers, |
| 90 query_args_l=query_args_l) |
| 91 if key: |
| 92 key.meta_generation = resp.getheader('x-goog-meta-generation') |
| 93 key.generation = resp.getheader('x-goog-generation') |
| 94 return key |
| 95 |
| 96 def copy_key(self, new_key_name, src_bucket_name, src_key_name, |
| 97 metadata=None, src_version_id=None, storage_class='STANDARD', |
| 98 preserve_acl=False, encrypt_key=False, headers=None, |
| 99 query_args=None, src_generation=None): |
| 100 if src_generation: |
| 101 headers['x-goog-copy-source-generation'] = src_generation |
| 102 super(Bucket, self).copy_key(new_key_name, src_bucket_name, |
| 103 src_key_name, metadata=metadata, |
| 104 storage_class=storage_class, |
| 105 preserve_acl=preserve_acl, |
| 106 encrypt_key=encrypt_key, headers=headers, |
| 107 query_args=query_args) |
| 108 |
| 109 def list_versions(self, prefix='', delimiter='', marker='', |
| 110 generation_marker='', headers=None): |
| 111 """ |
| 112 List versioned objects within a bucket. This returns an |
| 113 instance of an VersionedBucketListResultSet that automatically |
| 114 handles all of the result paging, etc. from GCS. You just need |
| 115 to keep iterating until there are no more results. Called |
| 116 with no arguments, this will return an iterator object across |
| 117 all keys within the bucket. |
| 118 |
| 119 :type prefix: string |
| 120 :param prefix: allows you to limit the listing to a particular |
| 121 prefix. For example, if you call the method with |
| 122 prefix='/foo/' then the iterator will only cycle through |
| 123 the keys that begin with the string '/foo/'. |
| 124 |
| 125 :type delimiter: string |
| 126 :param delimiter: can be used in conjunction with the prefix |
| 127 to allow you to organize and browse your keys |
| 128 hierarchically. See: |
| 129 https://developers.google.com/storage/docs/reference-headers#delimit
er |
| 130 for more details. |
| 131 |
| 132 :type marker: string |
| 133 :param marker: The "marker" of where you are in the result set |
| 134 |
| 135 :type generation_marker: string |
| 136 :param marker: The "generation marker" of where you are in the result |
| 137 set |
| 138 |
| 139 :rtype: |
| 140 :class:`boto.gs.bucketlistresultset.VersionedBucketListResultSet` |
| 141 :return: an instance of a BucketListResultSet that handles paging, etc |
| 142 """ |
| 143 return VersionedBucketListResultSet(self, prefix, delimiter, |
| 144 marker, generation_marker, |
| 145 headers) |
| 146 |
| 147 def delete_key(self, key_name, headers=None, version_id=None, |
| 148 mfa_token=None, generation=None): |
| 149 query_args_l = [] |
| 150 if generation: |
| 151 query_args_l.append('generation=%s' % generation) |
| 152 self._delete_key_internal(key_name, headers=headers, |
| 153 version_id=version_id, mfa_token=mfa_token, |
| 154 query_args_l=query_args_l) |
| 155 |
| 156 def set_acl(self, acl_or_str, key_name='', headers=None, version_id=None, |
| 157 generation=None): |
| 158 """Sets or changes a bucket's or key's ACL. The generation argument can |
| 159 be used to specify an object version, else we will modify the current |
| 160 version.""" |
| 161 key_name = key_name or '' |
| 162 query_args = STANDARD_ACL |
| 163 if generation: |
| 164 query_args += '&generation=%d' % generation |
| 165 if isinstance(acl_or_str, Policy): |
| 166 raise InvalidAclError('Attempt to set S3 Policy on GS ACL') |
| 167 elif isinstance(acl_or_str, ACL): |
| 168 self.set_xml_acl(acl_or_str.to_xml(), key_name, headers=headers, |
| 169 query_args=query_args) |
| 170 else: |
| 171 self.set_canned_acl(acl_or_str, key_name, headers=headers, |
| 172 generation=generation) |
| 173 |
| 174 def set_def_acl(self, acl_or_str, key_name='', headers=None): |
| 175 """sets or changes a bucket's default object acl. The key_name argument |
| 176 is ignored since keys have no default ACL property.""" |
| 177 if isinstance(acl_or_str, Policy): |
| 178 raise InvalidAclError('Attempt to set S3 Policy on GS ACL') |
| 179 elif isinstance(acl_or_str, ACL): |
| 180 self.set_def_xml_acl(acl_or_str.to_xml(), '', headers=headers) |
| 181 else: |
| 182 self.set_def_canned_acl(acl_or_str, '', headers=headers) |
| 183 |
| 184 def get_acl_helper(self, key_name, headers, query_args): |
| 185 """provides common functionality for get_acl() and get_def_acl()""" |
| 186 response = self.connection.make_request('GET', self.name, key_name, |
| 187 query_args=query_args, |
| 188 headers=headers) |
| 189 body = response.read() |
| 190 if response.status == 200: |
| 191 acl = ACL(self) |
| 192 h = handler.XmlHandler(acl, self) |
| 193 xml.sax.parseString(body, h) |
| 194 return acl |
| 195 else: |
| 196 raise self.connection.provider.storage_response_error( |
| 197 response.status, response.reason, body) |
| 198 |
| 199 def get_acl(self, key_name='', headers=None, version_id=None, |
| 200 generation=None): |
| 201 """returns a bucket's acl. We include a version_id argument |
| 202 to support a polymorphic interface for callers, however, |
| 203 version_id is not relevant for Google Cloud Storage buckets |
| 204 and is therefore ignored here.""" |
| 205 query_args = STANDARD_ACL |
| 206 if generation: |
| 207 query_args += '&generation=%d' % generation |
| 208 return self.get_acl_helper(key_name, headers, query_args) |
| 209 |
| 210 def get_def_acl(self, key_name='', headers=None): |
| 211 """returns a bucket's default object acl. The key_name argument is |
| 212 ignored since keys have no default ACL property.""" |
| 213 return self.get_acl_helper('', headers, DEF_OBJ_ACL) |
| 214 |
| 215 def set_canned_acl_helper(self, acl_str, key_name, headers, query_args): |
| 216 """provides common functionality for set_canned_acl() and |
| 217 set_def_canned_acl()""" |
| 218 assert acl_str in CannedACLStrings |
| 219 |
| 220 if headers: |
| 221 headers[self.connection.provider.acl_header] = acl_str |
| 222 else: |
| 223 headers={self.connection.provider.acl_header: acl_str} |
| 224 |
| 225 response = self.connection.make_request('PUT', self.name, key_name, |
| 226 headers=headers, query_args=query_args) |
| 227 body = response.read() |
| 228 if response.status != 200: |
| 229 raise self.connection.provider.storage_response_error( |
| 230 response.status, response.reason, body) |
| 231 |
| 232 def set_canned_acl(self, acl_str, key_name='', headers=None, |
| 233 version_id=None, generation=None): |
| 234 """sets or changes a bucket's acl to a predefined (canned) value. |
| 235 We include a version_id argument to support a polymorphic |
| 236 interface for callers, however, version_id is not relevant for |
| 237 Google Cloud Storage buckets and is therefore ignored here.""" |
| 238 query_args = STANDARD_ACL |
| 239 if generation: |
| 240 query_args += '&generation=%d' % generation |
| 241 return self.set_canned_acl_helper(acl_str, key_name, headers, |
| 242 query_args=query_args) |
| 243 |
| 244 def set_def_canned_acl(self, acl_str, key_name='', headers=None): |
| 245 """sets or changes a bucket's default object acl to a predefined |
| 246 (canned) value. The key_name argument is ignored since keys have no |
| 247 default ACL property.""" |
| 248 return self.set_canned_acl_helper(acl_str, '', headers, |
| 249 query_args=DEF_OBJ_ACL) |
| 250 |
| 251 def set_def_xml_acl(self, acl_str, key_name='', headers=None): |
| 252 """sets or changes a bucket's default object ACL. The key_name argument |
| 253 is ignored since keys have no default ACL property.""" |
| 254 return self.set_xml_acl(acl_str, '', headers, |
| 255 query_args=DEF_OBJ_ACL) |
| 256 |
| 257 def get_cors(self, headers=None): |
| 258 """returns a bucket's CORS XML""" |
| 259 response = self.connection.make_request('GET', self.name, |
| 260 query_args=CORS_ARG, |
| 261 headers=headers) |
| 262 body = response.read() |
| 263 if response.status == 200: |
| 264 # Success - parse XML and return Cors object. |
| 265 cors = Cors() |
| 266 h = handler.XmlHandler(cors, self) |
| 267 xml.sax.parseString(body, h) |
| 268 return cors |
| 269 else: |
| 270 raise self.connection.provider.storage_response_error( |
| 271 response.status, response.reason, body) |
| 272 |
| 273 def set_cors(self, cors, headers=None): |
| 274 """sets or changes a bucket's CORS XML.""" |
| 275 cors_xml = cors.encode('UTF-8') |
| 276 response = self.connection.make_request('PUT', self.name, |
| 277 data=cors_xml, |
| 278 query_args=CORS_ARG, |
| 279 headers=headers) |
| 280 body = response.read() |
| 281 if response.status != 200: |
| 282 raise self.connection.provider.storage_response_error( |
| 283 response.status, response.reason, body) |
| 284 |
| 285 def get_storage_class(self): |
| 286 """ |
| 287 Returns the StorageClass for the bucket. |
| 288 |
| 289 :rtype: str |
| 290 :return: The StorageClass for the bucket. |
| 291 """ |
| 292 response = self.connection.make_request('GET', self.name, |
| 293 query_args='storageClass') |
| 294 body = response.read() |
| 295 if response.status == 200: |
| 296 rs = ResultSet(self) |
| 297 h = handler.XmlHandler(rs, self) |
| 298 xml.sax.parseString(body, h) |
| 299 return rs.StorageClass |
| 300 else: |
| 301 raise self.connection.provider.storage_response_error( |
| 302 response.status, response.reason, body) |
| 303 |
| 304 |
| 305 # Method with same signature as boto.s3.bucket.Bucket.add_email_grant(), |
| 306 # to allow polymorphic treatment at application layer. |
| 307 def add_email_grant(self, permission, email_address, |
| 308 recursive=False, headers=None): |
| 309 """ |
| 310 Convenience method that provides a quick way to add an email grant |
| 311 to a bucket. This method retrieves the current ACL, creates a new |
| 312 grant based on the parameters passed in, adds that grant to the ACL |
| 313 and then PUT's the new ACL back to GCS. |
| 314 |
| 315 :type permission: string |
| 316 :param permission: The permission being granted. Should be one of: |
| 317 (READ, WRITE, FULL_CONTROL). |
| 318 |
| 319 :type email_address: string |
| 320 :param email_address: The email address associated with the GS |
| 321 account your are granting the permission to. |
| 322 |
| 323 :type recursive: boolean |
| 324 :param recursive: A boolean value to controls whether the call |
| 325 will apply the grant to all keys within the bucket |
| 326 or not. The default value is False. By passing a |
| 327 True value, the call will iterate through all keys |
| 328 in the bucket and apply the same grant to each key. |
| 329 CAUTION: If you have a lot of keys, this could take |
| 330 a long time! |
| 331 """ |
| 332 if permission not in GSPermissions: |
| 333 raise self.connection.provider.storage_permissions_error( |
| 334 'Unknown Permission: %s' % permission) |
| 335 acl = self.get_acl(headers=headers) |
| 336 acl.add_email_grant(permission, email_address) |
| 337 self.set_acl(acl, headers=headers) |
| 338 if recursive: |
| 339 for key in self: |
| 340 key.add_email_grant(permission, email_address, headers=headers) |
| 341 |
| 342 # Method with same signature as boto.s3.bucket.Bucket.add_user_grant(), |
| 343 # to allow polymorphic treatment at application layer. |
| 344 def add_user_grant(self, permission, user_id, recursive=False, headers=None)
: |
| 345 """ |
| 346 Convenience method that provides a quick way to add a canonical user |
| 347 grant to a bucket. This method retrieves the current ACL, creates a new |
| 348 grant based on the parameters passed in, adds that grant to the ACL and |
| 349 then PUTs the new ACL back to GCS. |
| 350 |
| 351 :type permission: string |
| 352 :param permission: The permission being granted. Should be one of: |
| 353 (READ|WRITE|FULL_CONTROL) |
| 354 |
| 355 :type user_id: string |
| 356 :param user_id: The canonical user id associated with the GS account |
| 357 you are granting the permission to. |
| 358 |
| 359 :type recursive: bool |
| 360 :param recursive: A boolean value to controls whether the call |
| 361 will apply the grant to all keys within the bucket |
| 362 or not. The default value is False. By passing a |
| 363 True value, the call will iterate through all keys |
| 364 in the bucket and apply the same grant to each key. |
| 365 CAUTION: If you have a lot of keys, this could take |
| 366 a long time! |
| 367 """ |
| 368 if permission not in GSPermissions: |
| 369 raise self.connection.provider.storage_permissions_error( |
| 370 'Unknown Permission: %s' % permission) |
| 371 acl = self.get_acl(headers=headers) |
| 372 acl.add_user_grant(permission, user_id) |
| 373 self.set_acl(acl, headers=headers) |
| 374 if recursive: |
| 375 for key in self: |
| 376 key.add_user_grant(permission, user_id, headers=headers) |
| 377 |
| 378 def add_group_email_grant(self, permission, email_address, recursive=False, |
| 379 headers=None): |
| 380 """ |
| 381 Convenience method that provides a quick way to add an email group |
| 382 grant to a bucket. This method retrieves the current ACL, creates a new |
| 383 grant based on the parameters passed in, adds that grant to the ACL and |
| 384 then PUT's the new ACL back to GCS. |
| 385 |
| 386 :type permission: string |
| 387 :param permission: The permission being granted. Should be one of: |
| 388 READ|WRITE|FULL_CONTROL |
| 389 See http://code.google.com/apis/storage/docs/developer-guide.html#au
thorization |
| 390 for more details on permissions. |
| 391 |
| 392 :type email_address: string |
| 393 :param email_address: The email address associated with the Google |
| 394 Group to which you are granting the permission. |
| 395 |
| 396 :type recursive: bool |
| 397 :param recursive: A boolean value to controls whether the call |
| 398 will apply the grant to all keys within the bucket |
| 399 or not. The default value is False. By passing a |
| 400 True value, the call will iterate through all keys |
| 401 in the bucket and apply the same grant to each key. |
| 402 CAUTION: If you have a lot of keys, this could take |
| 403 a long time! |
| 404 """ |
| 405 if permission not in GSPermissions: |
| 406 raise self.connection.provider.storage_permissions_error( |
| 407 'Unknown Permission: %s' % permission) |
| 408 acl = self.get_acl(headers=headers) |
| 409 acl.add_group_email_grant(permission, email_address) |
| 410 self.set_acl(acl, headers=headers) |
| 411 if recursive: |
| 412 for key in self: |
| 413 key.add_group_email_grant(permission, email_address, |
| 414 headers=headers) |
| 415 |
| 416 # Method with same input signature as boto.s3.bucket.Bucket.list_grants() |
| 417 # (but returning different object type), to allow polymorphic treatment |
| 418 # at application layer. |
| 419 def list_grants(self, headers=None): |
| 420 acl = self.get_acl(headers=headers) |
| 421 return acl.entries |
| 422 |
| 423 def disable_logging(self, headers=None): |
| 424 xml_str = '<?xml version="1.0" encoding="UTF-8"?><Logging/>' |
| 425 self.set_subresource('logging', xml_str, headers=headers) |
| 426 |
| 427 def enable_logging(self, target_bucket, target_prefix=None, headers=None): |
| 428 if isinstance(target_bucket, Bucket): |
| 429 target_bucket = target_bucket.name |
| 430 xml_str = '<?xml version="1.0" encoding="UTF-8"?><Logging>' |
| 431 xml_str = (xml_str + '<LogBucket>%s</LogBucket>' % target_bucket) |
| 432 if target_prefix: |
| 433 xml_str = (xml_str + |
| 434 '<LogObjectPrefix>%s</LogObjectPrefix>' % target_prefix) |
| 435 xml_str = xml_str + '</Logging>' |
| 436 |
| 437 self.set_subresource('logging', xml_str, headers=headers) |
| 438 |
| 439 def configure_website(self, main_page_suffix=None, error_key=None, |
| 440 headers=None): |
| 441 """ |
| 442 Configure this bucket to act as a website |
| 443 |
| 444 :type suffix: str |
| 445 :param suffix: Suffix that is appended to a request that is for a |
| 446 "directory" on the website endpoint (e.g. if the suffix |
| 447 is index.html and you make a request to |
| 448 samplebucket/images/ the data that is returned will |
| 449 be for the object with the key name images/index.html). |
| 450 The suffix must not be empty and must not include a |
| 451 slash character. This parameter is optional and the |
| 452 property is disabled if excluded. |
| 453 |
| 454 |
| 455 :type error_key: str |
| 456 :param error_key: The object key name to use when a 400 |
| 457 error occurs. This parameter is optional and the |
| 458 property is disabled if excluded. |
| 459 |
| 460 """ |
| 461 if main_page_suffix: |
| 462 main_page_frag = self.WebsiteMainPageFragment % main_page_suffix |
| 463 else: |
| 464 main_page_frag = '' |
| 465 |
| 466 if error_key: |
| 467 error_frag = self.WebsiteErrorFragment % error_key |
| 468 else: |
| 469 error_frag = '' |
| 470 |
| 471 body = self.WebsiteBody % (main_page_frag, error_frag) |
| 472 response = self.connection.make_request('PUT', self.name, data=body, |
| 473 query_args='websiteConfig', |
| 474 headers=headers) |
| 475 body = response.read() |
| 476 if response.status == 200: |
| 477 return True |
| 478 else: |
| 479 raise self.connection.provider.storage_response_error( |
| 480 response.status, response.reason, body) |
| 481 |
| 482 def get_website_configuration(self, headers=None): |
| 483 """ |
| 484 Returns the current status of website configuration on the bucket. |
| 485 |
| 486 :rtype: dict |
| 487 :returns: A dictionary containing a Python representation |
| 488 of the XML response from GCS. The overall structure is: |
| 489 |
| 490 * WebsiteConfiguration |
| 491 * MainPageSuffix: suffix that is appended to request that |
| 492 is for a "directory" on the website endpoint |
| 493 * NotFoundPage: name of an object to serve when site visitors |
| 494 encounter a 404 |
| 495 """ |
| 496 return self.get_website_configuration_xml(self, headers)[0] |
| 497 |
| 498 def get_website_configuration_with_xml(self, headers=None): |
| 499 """ |
| 500 Returns the current status of website configuration on the bucket as |
| 501 unparsed XML. |
| 502 |
| 503 :rtype: 2-Tuple |
| 504 :returns: 2-tuple containing: |
| 505 1) A dictionary containing a Python representation |
| 506 of the XML response from GCS. The overall structure is: |
| 507 * WebsiteConfiguration |
| 508 * MainPageSuffix: suffix that is appended to request that |
| 509 is for a "directory" on the website endpoint |
| 510 * NotFoundPage: name of an object to serve when site visitors |
| 511 encounter a 404 |
| 512 2) unparsed XML describing the bucket's website configuration. |
| 513 """ |
| 514 response = self.connection.make_request('GET', self.name, |
| 515 query_args='websiteConfig', headers=headers) |
| 516 body = response.read() |
| 517 boto.log.debug(body) |
| 518 |
| 519 if response.status != 200: |
| 520 raise self.connection.provider.storage_response_error( |
| 521 response.status, response.reason, body) |
| 522 |
| 523 e = boto.jsonresponse.Element() |
| 524 h = boto.jsonresponse.XmlHandler(e, None) |
| 525 h.parse(body) |
| 526 return e, body |
| 527 |
| 528 def delete_website_configuration(self, headers=None): |
| 529 self.configure_website(headers=headers) |
| 530 |
| 531 def get_versioning_status(self, headers=None): |
| 532 """ |
| 533 Returns the current status of versioning configuration on the bucket. |
| 534 |
| 535 :rtype: boolean |
| 536 :returns: boolean indicating whether or not versioning is enabled. |
| 537 """ |
| 538 response = self.connection.make_request('GET', self.name, |
| 539 query_args='versioning', |
| 540 headers=headers) |
| 541 body = response.read() |
| 542 boto.log.debug(body) |
| 543 if response.status != 200: |
| 544 raise self.connection.provider.storage_response_error( |
| 545 response.status, response.reason, body) |
| 546 resp_json = boto.jsonresponse.Element() |
| 547 boto.jsonresponse.XmlHandler(resp_json, None).parse(body) |
| 548 resp_json = resp_json['VersioningConfiguration'] |
| 549 return ('Status' in resp_json) and (resp_json['Status'] == 'Enabled') |
| 550 |
| 551 def configure_versioning(self, enabled, headers=None): |
| 552 if enabled == True: |
| 553 req_body = self.VersioningBody % ('Enabled') |
| 554 else: |
| 555 req_body = self.VersioningBody % ('Suspended') |
| 556 self.set_subresource('versioning', req_body, headers=headers) |
| OLD | NEW |