From 57c801c34fa16538f943fe4b8f0358f693a5039e Mon Sep 17 00:00:00 2001 From: Will Thames Date: Mon, 27 Feb 2017 10:55:57 +1000 Subject: [PATCH] RDS: split into more sensible modules RDS instance and snapshot facts modules Move common code into module_utils/rds.py This work is a precursor to new rds_snapshot and rds_instance modules Improve rds_snapshot_facts to latest AWS standards Improve exception handling, pagination etc. Version added Add clustered snapshot facts Add proper RETURN information for snapshots and cluster_snapshots pep8 Co-authored-by: Michael De La Rue --- .../cloud/amazon/rds_snapshot_facts.py | 381 ++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 lib/ansible/modules/cloud/amazon/rds_snapshot_facts.py diff --git a/lib/ansible/modules/cloud/amazon/rds_snapshot_facts.py b/lib/ansible/modules/cloud/amazon/rds_snapshot_facts.py new file mode 100644 index 00000000000..4ceb3131337 --- /dev/null +++ b/lib/ansible/modules/cloud/amazon/rds_snapshot_facts.py @@ -0,0 +1,381 @@ +#!/usr/bin/python +# Copyright (c) 2014-2017 Ansible Project +# Copyright (c) 2017, 2018 Will Thames +# Copyright (c) 2017, 2018 Michael De La Rue +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +ANSIBLE_METADATA = {'status': ['preview'], + 'supported_by': 'community', + 'metadata_version': '1.1'} + +DOCUMENTATION = ''' +--- +module: rds_snapshot_facts +version_added: "2.6" +short_description: obtain facts about one or more RDS snapshots +description: + - obtain facts about one or more RDS snapshots. These can be for unclustered snapshots or snapshots of clustered DBs (Aurora) + - Aurora snapshot facts may be obtained if no identifier parameters are passed or if one of the cluster parameters are passed. +options: + db_snapshot_identifier: + description: + - Name of an RDS (unclustered) snapshot. Mutually exclusive with I(db_instance_identifier), I(db_cluster_identifier), I(db_cluster_snapshot_identifier) + required: false + aliases: + - snapshot_name + db_instance_identifier: + description: + - RDS instance name for which to find snapshots. Mutually exclusive with I(db_snapshot_identifier), I(db_cluster_identifier), + I(db_cluster_snapshot_identifier) + required: false + db_cluster_identifier: + description: + - RDS cluster name for which to find snapshots. Mutually exclusive with I(db_snapshot_identifier), I(db_instance_identifier), + I(db_cluster_snapshot_identifier) + required: false + db_cluster_snapshot_identifier: + description: + - Name of an RDS cluster snapshot. Mutually exclusive with I(db_instance_identifier), I(db_snapshot_identifier), I(db_cluster_identifier) + required: false + snapshot_type: + description: + - Type of snapshot to find. By default both automated and manual + snapshots will be returned. + required: false + choices: ['automated', 'manual', 'shared', 'public'] +requirements: + - "python >= 2.6" + - "boto3" +author: + - "Will Thames (@willthames)" +extends_documentation_fragment: + - aws + - ec2 +''' + +EXAMPLES = ''' +# Get facts about an snapshot +- rds_snapshot_facts: + db_snapshot_identifier: snapshot_name + register: new_database_facts + +# Get all RDS snapshots for an RDS instance +- rds_snapshot_facts: + db_instance_identifier: helloworld-rds-master +''' + +RETURN = ''' +snapshots: + description: List of non-clustered snapshots + returned: When cluster parameters are not passed + type: complex + contains: + allocated_storage: + description: How many gigabytes of storage are allocated + returned: always + type: int + sample: 10 + availability_zone: + description: The availability zone of the database from which the snapshot was taken + returned: always + type: string + sample: us-west-2b + db_instance_identifier: + description: Database instance identifier + returned: always + type: string + sample: hello-world-rds + db_snapshot_arn: + description: Snapshot ARN + returned: always + type: string + sample: arn:aws:rds:us-west-2:111111111111:snapshot:rds:hello-world-rds-us1-2018-05-16-04-03 + db_snapshot_identifier: + description: Snapshot name + returned: always + type: string + sample: rds:hello-world-rds-us1-2018-05-16-04-03 + encrypted: + description: Whether the snapshot was encrypted + returned: always + type: bool + sample: true + engine: + description: Database engine + returned: always + type: string + sample: postgres + engine_version: + description: Database engine version + returned: always + type: string + sample: 9.5.10 + iam_database_authentication_enabled: + description: Whether database authentication through IAM is enabled + returned: always + type: bool + sample: false + instance_create_time: + description: Time the Instance was created + returned: always + type: string + sample: '2017-10-10T04:00:07.434000+00:00' + kms_key_id: + description: ID of the KMS Key encrypting the snapshot + returned: always + type: string + sample: arn:aws:kms:us-west-2:111111111111:key/abcd1234-1234-aaaa-0000-1234567890ab + license_model: + description: License model + returned: always + type: string + sample: postgresql-license + master_username: + description: Database master username + returned: always + type: string + sample: dbadmin + option_group_name: + description: Database option group name + returned: always + type: string + sample: default:postgres-9-5 + percent_progress: + description: Perecent progress of snapshot + returned: always + type: int + sample: 100 + snapshot_create_time: + description: Time snapshot was created + returned: always + type: string + sample: '2018-05-16T04:03:33.871000+00:00' + snapshot_type: + description: Type of snapshot + returned: always + type: string + sample: automated + status: + description: Status of snapshot + returned: always + type: string + sample: available + storage_type: + description: Storage type of underlying DB + returned: always + type: string + sample: gp2 + tags: + description: Snapshot tags + returned: always + type: complex + contains: {} + vpc_id: + description: ID of VPC containing the DB + returned: always + type: string + sample: vpc-abcd1234 +cluster_snapshots: + description: List of cluster snapshots + returned: always + type: complex + contains: + allocated_storage: + description: How many gigabytes of storage are allocated + returned: always + type: int + sample: 1 + availability_zones: + description: The availability zones of the database from which the snapshot was taken + returned: always + type: list + sample: + - ca-central-1a + - ca-central-1b + cluster_create_time: + description: Date and time the cluster was created + returned: always + type: string + sample: '2018-05-17T00:13:40.223000+00:00' + db_cluster_identifier: + description: Database cluster identifier + returned: always + type: string + sample: test-aurora-cluster + db_cluster_snapshot_arn: + description: ARN of the database snapshot + returned: always + type: string + sample: arn:aws:rds:ca-central-1:111111111111:cluster-snapshot:test-aurora-snapshot + db_cluster_snapshot_identifier: + description: Snapshot identifier + returned: always + type: string + sample: test-aurora-snapshot + engine: + description: Database engine + returned: always + type: string + sample: aurora + engine_version: + description: Database engine version + returned: always + type: string + sample: 5.6.10a + iam_database_authentication_enabled: + description: Whether database authentication through IAM is enabled + returned: always + type: bool + sample: false + kms_key_id: + description: ID of the KMS Key encrypting the snapshot + returned: always + type: string + sample: arn:aws:kms:ca-central-1:111111111111:key/abcd1234-abcd-1111-aaaa-0123456789ab + license_model: + description: License model + returned: always + type: string + sample: aurora + master_username: + description: Database master username + returned: always + type: string + sample: shertel + percent_progress: + description: Perecent progress of snapshot + returned: always + type: int + sample: 0 + port: + description: Database port + returned: always + type: int + sample: 0 + snapshot_create_time: + description: Date and time when the snapshot was created + returned: always + type: string + sample: '2018-05-17T00:23:23.731000+00:00' + snapshot_type: + description: Type of snapshot + returned: always + type: string + sample: manual + status: + description: Status of snapshot + returned: always + type: string + sample: creating + storage_encrypted: + description: Whether the snapshot is encrypted + returned: always + type: bool + sample: true + tags: + description: Tags of the snapshot + returned: always + type: complex + contains: {} + vpc_id: + description: VPC of the database + returned: always + type: string + sample: vpc-abcd1234 +''' + +from ansible.module_utils.aws.core import AnsibleAWSModule +from ansible.module_utils.ec2 import AWSRetry, boto3_tag_list_to_ansible_dict, camel_dict_to_snake_dict + +try: + import botocore +except BaseException: + pass # caught by imported HAS_BOTO3 + + +def common_snapshot_facts(module, conn, method, prefix, params): + paginator = conn.get_paginator(method) + try: + results = paginator.paginate(**params).build_full_result()['%ss' % prefix] + except conn.exceptions.from_code('%sNotFound' % prefix): + results = [] + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "trying to get snapshot information") + + for snapshot in results: + try: + snapshot['Tags'] = boto3_tag_list_to_ansible_dict(conn.list_tags_for_resource(ResourceName=snapshot['%sArn' % prefix], + aws_retry=True)['TagList']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Couldn't get tags for snapshot %s" % snapshot['%sIdentifier' % prefix]) + + return [camel_dict_to_snake_dict(snapshot, ignore_list=['Tags']) for snapshot in results] + + +def cluster_snapshot_facts(module, conn): + snapshot_name = module.params.get('db_cluster_snapshot_identifier') + snapshot_type = module.params.get('snapshot_type') + instance_name = module.params.get('db_cluster_instance_identifier') + + params = dict() + if snapshot_name: + params['DBClusterSnapshotIdentifier'] = snapshot_name + if instance_name: + params['DBClusterInstanceIdentifier'] = instance_name + if snapshot_type: + params['SnapshotType'] = snapshot_type + if snapshot_type == 'public': + params['IsPublic'] = True + elif snapshot_type == 'shared': + params['IsShared'] = True + + return common_snapshot_facts(module, conn, 'describe_db_cluster_snapshots', 'DBClusterSnapshot', params) + + +def standalone_snapshot_facts(module, conn): + snapshot_name = module.params.get('db_snapshot_identifier') + snapshot_type = module.params.get('snapshot_type') + instance_name = module.params.get('db_instance_identifier') + + params = dict() + if snapshot_name: + params['DBSnapshotIdentifier'] = snapshot_name + if instance_name: + params['DBInstanceIdentifier'] = instance_name + if snapshot_type: + params['SnapshotType'] = snapshot_type + if snapshot_type == 'public': + params['IsPublic'] = True + elif snapshot_type == 'shared': + params['IsShared'] = True + + return common_snapshot_facts(module, conn, 'describe_db_snapshots', 'DBSnapshot', params) + + +def main(): + argument_spec = dict( + db_snapshot_identifier=dict(aliases=['snapshot_name']), + db_instance_identifier=dict(), + db_cluster_identifier=dict(), + db_cluster_snapshot_identifier=dict(), + snapshot_type=dict(choices=['automated', 'manual', 'shared', 'public']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + mutually_exclusive=[['db_snapshot_identifier', 'db_instance_identifier', 'db_cluster_identifier', 'db_cluster_snapshot_identifier']] + ) + + conn = module.client('rds', retry_decorator=AWSRetry.jittered_backoff(retries=10)) + results = dict() + if not module.params['db_cluster_identifier'] and not module.params['db_cluster_snapshot_identifier']: + results['snapshots'] = standalone_snapshot_facts(module, conn) + if not module.params['db_snapshot_identifier'] and not module.params['db_instance_identifier']: + results['cluster_snapshots'] = cluster_snapshot_facts(module, conn) + + module.exit_json(changed=False, **results) + + +if __name__ == '__main__': + main()