diff --git a/cloud/amazon/s3_bucket.py b/cloud/amazon/s3_bucket.py index ec838d299f1..30c0e154242 100644 --- a/cloud/amazon/s3_bucket.py +++ b/cloud/amazon/s3_bucket.py @@ -16,9 +16,9 @@ DOCUMENTATION = ''' --- module: s3_bucket -short_description: Manage s3 buckets in AWS +short_description: Manage S3 buckets in AWS, Ceph, Walrus and FakeS3 description: - - Manage s3 buckets in AWS + - Manage S3 buckets in AWS, Ceph, Walrus and FakeS3 version_added: "2.0" author: "Rob White (@wimnat)" options: @@ -40,9 +40,13 @@ options: default: null s3_url: description: - - S3 URL endpoint for usage with Eucalypus, fakes3, etc. Otherwise assumes AWS + - S3 URL endpoint for usage with Ceph, Eucalypus, fakes3, etc. Otherwise assumes AWS default: null aliases: [ S3_URL ] + ceph: + description: + - Enable API compatibility with Ceph. It takes into account the S3 API subset working with Ceph in order to provide the same module behaviour where possible. + version_added: "2.2" requester_pays: description: - With Requester Pays buckets, the requester instead of the bucket owner pays the cost of the request and the data download from the bucket. @@ -78,6 +82,12 @@ EXAMPLES = ''' - s3_bucket: name: mys3bucket +# Create a simple s3 bucket on Ceph Rados Gateway +- s3_bucket: + name: mys3bucket + s3_url: http://your-ceph-rados-gateway-server.xxx + ceph: true + # Remove an s3 bucket and any keys it contains - s3_bucket: name: mys3bucket @@ -99,6 +109,9 @@ EXAMPLES = ''' import xml.etree.ElementTree as ET import urlparse +from ansible.module_utils.basic import * +from ansible.module_utils.ec2 import * + try: import boto.ec2 from boto.s3.connection import OrdinaryCallingFormat, Location @@ -130,8 +143,8 @@ def create_tags_container(tags): tags_obj.add_tag_set(tag_set) return tags_obj -def create_bucket(connection, module, location): - +def _create_bucket(connection, module, location): + policy = module.params.get("policy") name = module.params.get("name") requester_pays = module.params.get("requester_pays") @@ -141,11 +154,11 @@ def create_bucket(connection, module, location): try: bucket = connection.get_bucket(name) - except S3ResponseError, e: + except S3ResponseError as e: try: bucket = connection.create_bucket(name, location=location) changed = True - except S3CreateError, e: + except S3CreateError as e: module.fail_json(msg=e.message) # Versioning @@ -155,7 +168,7 @@ def create_bucket(connection, module, location): bucket.configure_versioning(versioning) changed = True versioning_status = bucket.get_versioning_status() - except S3ResponseError, e: + except S3ResponseError as e: module.fail_json(msg=e.message) elif not versioning_status and not versioning: # do nothing @@ -185,7 +198,7 @@ def create_bucket(connection, module, location): # Policy try: current_policy = bucket.get_policy() - except S3ResponseError, e: + except S3ResponseError as e: if e.error_code == "NoSuchBucketPolicy": current_policy = None else: @@ -200,7 +213,7 @@ def create_bucket(connection, module, location): bucket.set_policy(policy) changed = True current_policy = bucket.get_policy() - except S3ResponseError, e: + except S3ResponseError as e: module.fail_json(msg=e.message) elif current_policy is None and policy is not None: @@ -210,7 +223,7 @@ def create_bucket(connection, module, location): bucket.set_policy(policy) changed = True current_policy = bucket.get_policy() - except S3ResponseError, e: + except S3ResponseError as e: module.fail_json(msg=e.message) elif current_policy is not None and policy is None: @@ -218,7 +231,7 @@ def create_bucket(connection, module, location): bucket.delete_policy() changed = True current_policy = bucket.get_policy() - except S3ResponseError, e: + except S3ResponseError as e: if e.error_code == "NoSuchBucketPolicy": current_policy = None else: @@ -232,7 +245,7 @@ def create_bucket(connection, module, location): try: current_tags = bucket.get_tags() tag_set = TagSet() - except S3ResponseError, e: + except S3ResponseError as e: if e.error_code == "NoSuchTagSet": current_tags = None else: @@ -253,12 +266,12 @@ def create_bucket(connection, module, location): bucket.delete_tags() current_tags_dict = tags changed = True - except S3ResponseError, e: + except S3ResponseError as e: module.fail_json(msg=e.message) module.exit_json(changed=changed, name=bucket.name, versioning=versioning_status, requester_pays=requester_pays_status, policy=current_policy, tags=current_tags_dict) -def destroy_bucket(connection, module): +def _destroy_bucket(connection, module): force = module.params.get("force") name = module.params.get("name") @@ -266,7 +279,7 @@ def destroy_bucket(connection, module): try: bucket = connection.get_bucket(name) - except S3ResponseError, e: + except S3ResponseError as e: if e.error_code != "NoSuchBucket": module.fail_json(msg=e.message) else: @@ -279,17 +292,50 @@ def destroy_bucket(connection, module): for key in bucket.list(): key.delete() - except BotoServerError, e: + except BotoServerError as e: module.fail_json(msg=e.message) try: bucket = connection.delete_bucket(name) changed = True - except S3ResponseError, e: + except S3ResponseError as e: module.fail_json(msg=e.message) module.exit_json(changed=changed) +def _create_bucket_ceph(connection, module, location): + + name = module.params.get("name") + + changed = False + + try: + bucket = connection.get_bucket(name) + except S3ResponseError as e: + try: + bucket = connection.create_bucket(name, location=location) + changed = True + except S3CreateError as e: + module.fail_json(msg=e.message) + + module.exit_json(changed=changed) + +def _destroy_bucket_ceph(connection, module): + + _destroy_bucket(connection, module) + +def create_bucket(connection, module, location, flavour='aws'): + if flavour == 'ceph': + _create_bucket_ceph(connection, module, location) + else: + _create_bucket(connection, module, location) + +def destroy_bucket(connection, module, flavour='aws'): + if flavour == 'ceph': + _destroy_bucket_ceph(connection, module) + else: + _destroy_bucket(connection, module) + def is_fakes3(s3_url): """ Return True if s3_url has scheme fakes3:// """ if s3_url is not None: @@ -319,7 +365,8 @@ def main(): s3_url = dict(aliases=['S3_URL']), state = dict(default='present', choices=['present', 'absent']), tags = dict(required=None, default={}, type='dict'), - versioning = dict(default='no', type='bool') + versioning = dict(default='no', type='bool'), + ceph = dict(default='no', type='bool') ) ) @@ -344,10 +391,27 @@ def main(): if not s3_url and 'S3_URL' in os.environ: s3_url = os.environ['S3_URL'] + ceph = module.params.get('ceph') + + if ceph and not s3_url: + module.fail_json(msg='ceph flavour requires s3_url') + + flavour = 'aws' + # Look at s3_url and tweak connection settings # if connecting to Walrus or fakes3 try: - if is_fakes3(s3_url): + if s3_url and ceph: + ceph = urlparse.urlparse(s3_url) + connection = boto.connect_s3( + host=ceph.hostname, + port=ceph.port, + is_secure=ceph.scheme == 'https', + calling_format=OrdinaryCallingFormat(), + **aws_connect_params + ) + flavour = 'ceph' + elif is_fakes3(s3_url): fakes3 = urlparse.urlparse(s3_url) connection = S3Connection( is_secure=fakes3.scheme == 'fakes3s', @@ -365,9 +429,9 @@ def main(): if connection is None: connection = boto.connect_s3(**aws_connect_params) - except boto.exception.NoAuthHandlerFound, e: + except boto.exception.NoAuthHandlerFound as e: module.fail_json(msg='No Authentication Handler found: %s ' % str(e)) - except Exception, e: + except Exception as e: module.fail_json(msg='Failed to connect to S3: %s' % str(e)) if connection is None: # this should never happen @@ -376,12 +440,9 @@ def main(): state = module.params.get("state") if state == 'present': - create_bucket(connection, module, location) + create_bucket(connection, module, location, flavour=flavour) elif state == 'absent': - destroy_bucket(connection, module) - -from ansible.module_utils.basic import * -from ansible.module_utils.ec2 import * + destroy_bucket(connection, module, flavour=flavour) if __name__ == '__main__': main()