Skip to content

Commit

Permalink
Rename rds_snapshot to rds_instance_snapshot (ansible-collections#783)
Browse files Browse the repository at this point in the history
Rename rds_snapshot to rds_instance_snapshot

SUMMARY

Rename rds_snapshot to rds_instance_snapshot since rds_snapshot only handles snapshotting of DB instances. A new module for snapshotting RDS clusters will be added in a future PR.

ISSUE TYPE

New Module Pull Request

COMPONENT NAME

rds_snapshot

Reviewed-by: Mark Chappell <None>
Reviewed-by: None <None>

This commit was initially merged in https://github.com/ansible-collections/community.aws
See: ansible-collections/community.aws@4c44372
  • Loading branch information
alinabuzachis authored and goneri committed Sep 21, 2022
1 parent 7a253ed commit abdcca8
Show file tree
Hide file tree
Showing 2 changed files with 347 additions and 1 deletion.
346 changes: 346 additions & 0 deletions plugins/modules/rds_instance_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
#!/usr/bin/python
# Copyright (c) 2014 Ansible Project
# Copyright (c) 2017, 2018, 2019 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)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = '''
---
module: rds_instance_snapshot
version_added: 1.0.0
short_description: manage Amazon RDS snapshots.
description:
- Creates or deletes RDS snapshots.
options:
state:
description:
- Specify the desired state of the snapshot.
default: present
choices: [ 'present', 'absent']
type: str
db_snapshot_identifier:
description:
- The snapshot to manage.
required: true
aliases:
- id
- snapshot_id
type: str
db_instance_identifier:
description:
- Database instance identifier. Required when state is present.
aliases:
- instance_id
type: str
wait:
description:
- Whether or not to wait for snapshot creation or deletion.
type: bool
default: 'no'
wait_timeout:
description:
- how long before wait gives up, in seconds.
default: 300
type: int
tags:
description:
- tags dict to apply to a snapshot.
type: dict
purge_tags:
description:
- whether to remove tags not present in the C(tags) parameter.
default: True
type: bool
author:
- "Will Thames (@willthames)"
- "Michael De La Rue (@mikedlr)"
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
'''

EXAMPLES = '''
- name: Create snapshot
community.aws.rds_instance_snapshot:
db_instance_identifier: new-database
db_snapshot_identifier: new-database-snapshot
- name: Delete snapshot
community.aws.rds_instance_snapshot:
db_snapshot_identifier: new-database-snapshot
state: absent
'''

RETURN = '''
allocated_storage:
description: How much storage is allocated in GB.
returned: always
type: int
sample: 20
availability_zone:
description: Availability zone of the database from which the snapshot was created.
returned: always
type: str
sample: us-west-2a
db_instance_identifier:
description: Database from which the snapshot was created.
returned: always
type: str
sample: ansible-test-16638696
db_snapshot_arn:
description: Amazon Resource Name for the snapshot.
returned: always
type: str
sample: arn:aws:rds:us-west-2:123456789012:snapshot:ansible-test-16638696-test-snapshot
db_snapshot_identifier:
description: Name of the snapshot.
returned: always
type: str
sample: ansible-test-16638696-test-snapshot
dbi_resource_id:
description: The identifier for the source DB instance, which can't be changed and which is unique to an AWS Region.
returned: always
type: str
sample: db-MM4P2U35RQRAMWD3QDOXWPZP4U
encrypted:
description: Whether the snapshot is encrypted.
returned: always
type: bool
sample: false
engine:
description: Engine of the database from which the snapshot was created.
returned: always
type: str
sample: mariadb
engine_version:
description: Version of the database from which the snapshot was created.
returned: always
type: str
sample: 10.2.21
iam_database_authentication_enabled:
description: Whether IAM database authentication is enabled.
returned: always
type: bool
sample: false
instance_create_time:
description: Creation time of the instance from which the snapshot was created.
returned: always
type: str
sample: '2019-06-15T10:15:56.221000+00:00'
license_model:
description: License model of the database.
returned: always
type: str
sample: general-public-license
master_username:
description: Master username of the database.
returned: always
type: str
sample: test
option_group_name:
description: Option group of the database.
returned: always
type: str
sample: default:mariadb-10-2
percent_progress:
description: How much progress has been made taking the snapshot. Will be 100 for an available snapshot.
returned: always
type: int
sample: 100
port:
description: Port on which the database is listening.
returned: always
type: int
sample: 3306
processor_features:
description: List of processor features of the database.
returned: always
type: list
sample: []
snapshot_create_time:
description: Creation time of the snapshot.
returned: always
type: str
sample: '2019-06-15T10:46:23.776000+00:00'
snapshot_type:
description: How the snapshot was created (always manual for this module!).
returned: always
type: str
sample: manual
status:
description: Status of the snapshot.
returned: always
type: str
sample: available
storage_type:
description: Storage type of the database.
returned: always
type: str
sample: gp2
tags:
description: Tags applied to the snapshot.
returned: always
type: complex
contains: {}
vpc_id:
description: ID of the VPC in which the DB lives.
returned: always
type: str
sample: vpc-09ff232e222710ae0
'''

try:
import botocore
except ImportError:
pass # protected by AnsibleAWSModule

# import module snippets
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict, AWSRetry, compare_aws_tags
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict, ansible_dict_to_boto3_tag_list


def get_snapshot(client, module, snapshot_id):
try:
response = client.describe_db_snapshots(DBSnapshotIdentifier=snapshot_id)
except client.exceptions.DBSnapshotNotFoundFault:
return None
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
module.fail_json_aws(e, msg="Couldn't get snapshot {0}".format(snapshot_id))
return response['DBSnapshots'][0]


def snapshot_to_facts(client, module, snapshot):
try:
snapshot['Tags'] = boto3_tag_list_to_ansible_dict(client.list_tags_for_resource(ResourceName=snapshot['DBSnapshotArn'],
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['DBSnapshotIdentifier'])
except KeyError:
module.fail_json(msg=str(snapshot))

return camel_dict_to_snake_dict(snapshot, ignore_list=['Tags'])


def wait_for_snapshot_status(client, module, db_snapshot_id, waiter_name):
if not module.params['wait']:
return
timeout = module.params['wait_timeout']
try:
client.get_waiter(waiter_name).wait(DBSnapshotIdentifier=db_snapshot_id,
WaiterConfig=dict(
Delay=5,
MaxAttempts=int((timeout + 2.5) / 5)
))
except botocore.exceptions.WaiterError as e:
if waiter_name == 'db_snapshot_deleted':
msg = "Failed to wait for DB snapshot {0} to be deleted".format(db_snapshot_id)
else:
msg = "Failed to wait for DB snapshot {0} to be available".format(db_snapshot_id)
module.fail_json_aws(e, msg=msg)
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
module.fail_json_aws(e, msg="Failed with an unexpected error while waiting for the DB cluster {0}".format(db_snapshot_id))


def ensure_snapshot_absent(client, module):
snapshot_name = module.params.get('db_snapshot_identifier')
changed = False

snapshot = get_snapshot(client, module, snapshot_name)
if snapshot and snapshot['Status'] != 'deleting':
try:
client.delete_db_snapshot(DBSnapshotIdentifier=snapshot_name)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="trying to delete snapshot")

# If we're not waiting for a delete to complete then we're all done
# so just return
if not snapshot or not module.params.get('wait'):
return dict(changed=changed)
try:
wait_for_snapshot_status(client, module, snapshot_name, 'db_snapshot_deleted')
return dict(changed=changed)
except client.exceptions.DBSnapshotNotFoundFault:
return dict(changed=changed)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, "awaiting snapshot deletion")


def ensure_tags(client, module, resource_arn, existing_tags, tags, purge_tags):
if tags is None:
return False
tags_to_add, tags_to_remove = compare_aws_tags(existing_tags, tags, purge_tags)
changed = bool(tags_to_add or tags_to_remove)
if tags_to_add:
try:
client.add_tags_to_resource(ResourceName=resource_arn, Tags=ansible_dict_to_boto3_tag_list(tags_to_add))
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, "Couldn't add tags to snapshot {0}".format(resource_arn))
if tags_to_remove:
try:
client.remove_tags_from_resource(ResourceName=resource_arn, TagKeys=tags_to_remove)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, "Couldn't remove tags from snapshot {0}".format(resource_arn))
return changed


def ensure_snapshot_present(client, module):
db_instance_identifier = module.params.get('db_instance_identifier')
snapshot_name = module.params.get('db_snapshot_identifier')
changed = False
snapshot = get_snapshot(client, module, snapshot_name)
if not snapshot:
try:
snapshot = client.create_db_snapshot(DBSnapshotIdentifier=snapshot_name,
DBInstanceIdentifier=db_instance_identifier)['DBSnapshot']
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="trying to create db snapshot")

if module.params.get('wait'):
wait_for_snapshot_status(client, module, snapshot_name, 'db_snapshot_available')

existing_tags = boto3_tag_list_to_ansible_dict(client.list_tags_for_resource(ResourceName=snapshot['DBSnapshotArn'],
aws_retry=True)['TagList'])
desired_tags = module.params['tags']
purge_tags = module.params['purge_tags']
changed |= ensure_tags(client, module, snapshot['DBSnapshotArn'], existing_tags, desired_tags, purge_tags)

snapshot = get_snapshot(client, module, snapshot_name)

return dict(changed=changed, **snapshot_to_facts(client, module, snapshot))


def main():

module = AnsibleAWSModule(
argument_spec=dict(
state=dict(choices=['present', 'absent'], default='present'),
db_snapshot_identifier=dict(aliases=['id', 'snapshot_id'], required=True),
db_instance_identifier=dict(aliases=['instance_id']),
wait=dict(type='bool', default=False),
wait_timeout=dict(type='int', default=300),
tags=dict(type='dict'),
purge_tags=dict(type='bool', default=True),
),
required_if=[['state', 'present', ['db_instance_identifier']]]
)

client = module.client('rds', retry_decorator=AWSRetry.jittered_backoff(retries=10, catch_extra_error_codes=['DBSnapshotNotFound']))

if module.params['state'] == 'absent':
ret_dict = ensure_snapshot_absent(client, module)
else:
ret_dict = ensure_snapshot_present(client, module)

module.exit_json(**ret_dict)


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@

always:
- name: remove snapshot
rds_snapshot:
rds_instance_snapshot:
db_snapshot_identifier: "{{ resource_prefix }}-test-snapshot"
state: absent
wait: false
Expand Down

0 comments on commit abdcca8

Please sign in to comment.