Dre4m Shell
Server IP : 85.214.239.14  /  Your IP : 3.135.195.180
Web Server : Apache/2.4.62 (Debian)
System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64
User : www-data ( 33)
PHP Version : 7.4.18
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : OFF
Directory :  /usr/lib/python3/dist-packages/ansible_collections/amazon/aws/plugins/module_utils/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME SHELL ]     

Current File : /usr/lib/python3/dist-packages/ansible_collections/amazon/aws/plugins/module_utils/rds.py
# Copyright: (c) 2018, Ansible Project
# 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

from collections import namedtuple
from time import sleep

try:
    from botocore.exceptions import BotoCoreError, ClientError, WaiterError
except ImportError:
    pass

from ansible.module_utils._text import to_text
from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict

from .ec2 import AWSRetry
from .ec2 import ansible_dict_to_boto3_tag_list
from .ec2 import boto3_tag_list_to_ansible_dict
from .ec2 import compare_aws_tags
from .waiters import get_waiter

Boto3ClientMethod = namedtuple('Boto3ClientMethod', ['name', 'waiter', 'operation_description', 'resource', 'retry_codes'])
# Whitelist boto3 client methods for cluster and instance resources
cluster_method_names = [
    'create_db_cluster', 'restore_db_cluster_from_snapshot', 'restore_db_cluster_from_s3',
    'restore_db_cluster_to_point_in_time', 'modify_db_cluster', 'delete_db_cluster', 'add_tags_to_resource',
    'remove_tags_from_resource', 'list_tags_for_resource', 'promote_read_replica_db_cluster'
]
instance_method_names = [
    'create_db_instance', 'restore_db_instance_to_point_in_time', 'restore_db_instance_from_s3',
    'restore_db_instance_from_db_snapshot', 'create_db_instance_read_replica', 'modify_db_instance',
    'delete_db_instance', 'add_tags_to_resource', 'remove_tags_from_resource', 'list_tags_for_resource',
    'promote_read_replica', 'stop_db_instance', 'start_db_instance', 'reboot_db_instance', 'add_role_to_db_instance',
    'remove_role_from_db_instance'
]

cluster_snapshot_method_names = [
    'create_db_cluster_snapshot', 'delete_db_cluster_snapshot', 'add_tags_to_resource', 'remove_tags_from_resource',
    'list_tags_for_resource', 'copy_db_cluster_snapshot'
]

instance_snapshot_method_names = [
    'create_db_snapshot', 'delete_db_snapshot', 'add_tags_to_resource', 'remove_tags_from_resource',
    'copy_db_snapshot', 'list_tags_for_resource'
]


def get_rds_method_attribute(method_name, module):
    '''
    Returns rds attributes of the specified method.

        Parameters:
            method_name (str): RDS method to call
            module: AnsibleAWSModule

        Returns:
            Boto3ClientMethod (dict):
                name (str): Name of method
                waiter (str): Name of waiter associated with given method
                operation_description (str): Description of method
                resource (str): Type of resource this method applies to
                                One of ['instance', 'cluster', 'instance_snapshot', 'cluster_snapshot']
                retry_codes (list): List of extra error codes to retry on

        Raises:
            NotImplementedError if wait is True but no waiter can be found for specified method
    '''
    waiter = ''
    readable_op = method_name.replace('_', ' ').replace('db', 'DB')
    resource = ''
    retry_codes = []
    if method_name in cluster_method_names and 'new_db_cluster_identifier' in module.params:
        resource = 'cluster'
        if method_name == 'delete_db_cluster':
            waiter = 'cluster_deleted'
        else:
            waiter = 'cluster_available'
        # Handle retry codes
        if method_name == 'restore_db_cluster_from_snapshot':
            retry_codes = ['InvalidDBClusterSnapshotState']
        else:
            retry_codes = ['InvalidDBClusterState']
    elif method_name in instance_method_names and 'new_db_instance_identifier' in module.params:
        resource = 'instance'
        if method_name == 'delete_db_instance':
            waiter = 'db_instance_deleted'
        elif method_name == 'stop_db_instance':
            waiter = 'db_instance_stopped'
        elif method_name == 'add_role_to_db_instance':
            waiter = 'role_associated'
        elif method_name == 'remove_role_from_db_instance':
            waiter = 'role_disassociated'
        elif method_name == 'promote_read_replica':
            waiter = 'read_replica_promoted'
        else:
            waiter = 'db_instance_available'
        # Handle retry codes
        if method_name == 'restore_db_instance_from_db_snapshot':
            retry_codes = ['InvalidDBSnapshotState']
        else:
            retry_codes = ['InvalidDBInstanceState', 'InvalidDBSecurityGroupState']
    elif method_name in cluster_snapshot_method_names and 'db_cluster_snapshot_identifier' in module.params:
        resource = 'cluster_snapshot'
        if method_name == 'delete_db_cluster_snapshot':
            waiter = 'db_cluster_snapshot_deleted'
            retry_codes = ['InvalidDBClusterSnapshotState']
        elif method_name == 'create_db_cluster_snapshot':
            waiter = 'db_cluster_snapshot_available'
            retry_codes = ['InvalidDBClusterState']
        else:
            # Tagging
            waiter = 'db_cluster_snapshot_available'
            retry_codes = ['InvalidDBClusterSnapshotState']
    elif method_name in instance_snapshot_method_names and 'db_snapshot_identifier' in module.params:
        resource = 'instance_snapshot'
        if method_name == 'delete_db_snapshot':
            waiter = 'db_snapshot_deleted'
            retry_codes = ['InvalidDBSnapshotState']
        elif method_name == 'create_db_snapshot':
            waiter = 'db_snapshot_available'
            retry_codes = ['InvalidDBInstanceState']
        else:
            # Tagging
            waiter = 'db_snapshot_available'
            retry_codes = ['InvalidDBSnapshotState']
    else:
        if module.params.get('wait'):
            raise NotImplementedError("method {0} hasn't been added to the list of accepted methods to use a waiter in module_utils/rds.py".format(method_name))

    return Boto3ClientMethod(name=method_name, waiter=waiter, operation_description=readable_op,
                             resource=resource, retry_codes=retry_codes)


def get_final_identifier(method_name, module):
    updated_identifier = None
    apply_immediately = module.params.get('apply_immediately')
    resource = get_rds_method_attribute(method_name, module).resource
    if resource == 'cluster':
        identifier = module.params['db_cluster_identifier']
        updated_identifier = module.params['new_db_cluster_identifier']
    elif resource == 'instance':
        identifier = module.params['db_instance_identifier']
        updated_identifier = module.params['new_db_instance_identifier']
    elif resource == 'instance_snapshot':
        identifier = module.params['db_snapshot_identifier']
    elif resource == 'cluster_snapshot':
        identifier = module.params['db_cluster_snapshot_identifier']
    else:
        raise NotImplementedError("method {0} hasn't been added to the list of accepted methods in module_utils/rds.py".format(method_name))
    if not module.check_mode and updated_identifier and apply_immediately:
        identifier = updated_identifier
    return identifier


def handle_errors(module, exception, method_name, parameters):

    if not isinstance(exception, ClientError):
        module.fail_json_aws(exception, msg="Unexpected failure for method {0} with parameters {1}".format(method_name, parameters))

    changed = True
    error_code = exception.response['Error']['Code']
    if (
        method_name in ('modify_db_instance', 'modify_db_cluster') and
        error_code == 'InvalidParameterCombination'
    ):
        if 'No modifications were requested' in to_text(exception):
            changed = False
        elif 'ModifyDbCluster API' in to_text(exception):
            module.fail_json_aws(exception, msg='It appears you are trying to modify attributes that are managed at the cluster level. Please see rds_cluster')
        else:
            module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description))
    elif method_name == 'promote_read_replica' and error_code == 'InvalidDBInstanceState':
        if 'DB Instance is not a read replica' in to_text(exception):
            changed = False
        else:
            module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description))
    elif method_name == 'promote_read_replica_db_cluster' and error_code == 'InvalidDBClusterStateFault':
        if 'DB Cluster that is not a read replica' in to_text(exception):
            changed = False
        else:
            module.fail_json_aws(
                exception,
                msg="Unable to {0}".format(get_rds_method_attribute(method_name, module).operation_description),
            )
    elif method_name == "create_db_cluster" and error_code == "InvalidParameterValue":
        accepted_engines = ["aurora", "aurora-mysql", "aurora-postgresql", "mysql", "postgres"]
        if parameters.get("Engine") not in accepted_engines:
            module.fail_json_aws(
                exception, msg="DB engine {0} should be one of {1}".format(parameters.get("Engine"), accepted_engines)
            )
        else:
            module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description))
    else:
        module.fail_json_aws(exception, msg='Unable to {0}'.format(get_rds_method_attribute(method_name, module).operation_description))

    return changed


def call_method(client, module, method_name, parameters):
    result = {}
    changed = True
    if not module.check_mode:
        wait = module.params.get('wait')
        retry_codes = get_rds_method_attribute(method_name, module).retry_codes
        method = getattr(client, method_name)
        try:
            result = AWSRetry.jittered_backoff(catch_extra_error_codes=retry_codes)(method)(**parameters)
        except (BotoCoreError, ClientError) as e:
            changed = handle_errors(module, e, method_name, parameters)

        if wait and changed:
            identifier = get_final_identifier(method_name, module)
            wait_for_status(client, module, identifier, method_name)
    return result, changed


def wait_for_instance_status(client, module, db_instance_id, waiter_name):
    def wait(client, db_instance_id, waiter_name):
        try:
            waiter = client.get_waiter(waiter_name)
        except ValueError:
            # using a waiter in module_utils/waiters.py
            waiter = get_waiter(client, waiter_name)
        waiter.wait(WaiterConfig={'Delay': 60, 'MaxAttempts': 60}, DBInstanceIdentifier=db_instance_id)

    waiter_expected_status = {
        'db_instance_deleted': 'deleted',
        'db_instance_stopped': 'stopped',
    }
    expected_status = waiter_expected_status.get(waiter_name, 'available')
    for _wait_attempts in range(0, 10):
        try:
            wait(client, db_instance_id, waiter_name)
            break
        except WaiterError as e:
            # Instance may be renamed and AWSRetry doesn't handle WaiterError
            if e.last_response.get('Error', {}).get('Code') == 'DBInstanceNotFound':
                sleep(10)
                continue
            module.fail_json_aws(e, msg='Error while waiting for DB instance {0} to be {1}'.format(db_instance_id, expected_status))
        except (BotoCoreError, ClientError) as e:
            module.fail_json_aws(e, msg='Unexpected error while waiting for DB instance {0} to be {1}'.format(
                db_instance_id, expected_status)
            )


def wait_for_cluster_status(client, module, db_cluster_id, waiter_name):
    try:
        get_waiter(client, waiter_name).wait(DBClusterIdentifier=db_cluster_id)
    except WaiterError as e:
        if waiter_name == 'cluster_deleted':
            msg = "Failed to wait for DB cluster {0} to be deleted".format(db_cluster_id)
        else:
            msg = "Failed to wait for DB cluster {0} to be available".format(db_cluster_id)
        module.fail_json_aws(e, msg=msg)
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg="Failed with an unexpected error while waiting for the DB cluster {0}".format(db_cluster_id))


def wait_for_instance_snapshot_status(client, module, db_snapshot_id, waiter_name):
    try:
        client.get_waiter(waiter_name).wait(DBSnapshotIdentifier=db_snapshot_id)
    except 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 (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg="Failed with an unexpected error while waiting for the DB snapshot {0}".format(db_snapshot_id))


def wait_for_cluster_snapshot_status(client, module, db_snapshot_id, waiter_name):
    try:
        client.get_waiter(waiter_name).wait(DBClusterSnapshotIdentifier=db_snapshot_id)
    except WaiterError as e:
        if waiter_name == 'db_cluster_snapshot_deleted':
            msg = "Failed to wait for DB cluster snapshot {0} to be deleted".format(db_snapshot_id)
        else:
            msg = "Failed to wait for DB cluster snapshot {0} to be available".format(db_snapshot_id)
        module.fail_json_aws(e, msg=msg)
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg="Failed with an unexpected error while waiting for the DB cluster snapshot {0}".format(db_snapshot_id))


def wait_for_status(client, module, identifier, method_name):
    rds_method_attributes = get_rds_method_attribute(method_name, module)
    waiter_name = rds_method_attributes.waiter
    resource = rds_method_attributes.resource

    if resource == 'cluster':
        wait_for_cluster_status(client, module, identifier, waiter_name)
    elif resource == 'instance':
        wait_for_instance_status(client, module, identifier, waiter_name)
    elif resource == 'instance_snapshot':
        wait_for_instance_snapshot_status(client, module, identifier, waiter_name)
    elif resource == 'cluster_snapshot':
        wait_for_cluster_snapshot_status(client, module, identifier, waiter_name)


def get_tags(client, module, resource_arn):
    try:
        return boto3_tag_list_to_ansible_dict(
            client.list_tags_for_resource(ResourceName=resource_arn)['TagList']
        )
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e, msg="Unable to describe tags")


def arg_spec_to_rds_params(options_dict):
    tags = options_dict.pop('tags')
    has_processor_features = False
    if 'processor_features' in options_dict:
        has_processor_features = True
        processor_features = options_dict.pop('processor_features')
    camel_options = snake_dict_to_camel_dict(options_dict, capitalize_first=True)
    for key in list(camel_options.keys()):
        for old, new in (('Db', 'DB'), ('Iam', 'IAM'), ('Az', 'AZ')):
            if old in key:
                camel_options[key.replace(old, new)] = camel_options.pop(key)
    camel_options['Tags'] = tags
    if has_processor_features:
        camel_options['ProcessorFeatures'] = processor_features
    return camel_options


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:
        call_method(
            client, module, method_name='add_tags_to_resource',
            parameters={'ResourceName': resource_arn, 'Tags': ansible_dict_to_boto3_tag_list(tags_to_add)}
        )
    if tags_to_remove:
        call_method(
            client, module, method_name='remove_tags_from_resource',
            parameters={'ResourceName': resource_arn, 'TagKeys': tags_to_remove}
        )
    return changed


def compare_iam_roles(existing_roles, target_roles, purge_roles):
    '''
    Returns differences between target and existing IAM roles

        Parameters:
            existing_roles (list): Existing IAM roles
            target_roles (list): Target IAM roles
            purge_roles (bool): Remove roles not in target_roles if True

        Returns:
            roles_to_add (list): List of IAM roles to add
            roles_to_delete (list): List of IAM roles to delete
    '''
    existing_roles = [dict((k, v) for k, v in role.items() if k != 'status') for role in existing_roles]
    roles_to_add = [role for role in target_roles if role not in existing_roles]
    roles_to_remove = [role for role in existing_roles if role not in target_roles] if purge_roles else []
    return roles_to_add, roles_to_remove


def update_iam_roles(client, module, instance_id, roles_to_add, roles_to_remove):
    '''
    Update a DB instance's associated IAM roles

        Parameters:
            client: RDS client
            module: AnsibleAWSModule
            instance_id (str): DB's instance ID
            roles_to_add (list): List of IAM roles to add
            roles_to_delete (list): List of IAM roles to delete

        Returns:
            changed (bool): True if changes were successfully made to DB instance's IAM roles; False if not
    '''
    for role in roles_to_remove:
        params = {'DBInstanceIdentifier': instance_id,
                  'RoleArn': role['role_arn'],
                  'FeatureName': role['feature_name']}
        _result, changed = call_method(client, module, method_name='remove_role_from_db_instance', parameters=params)
    for role in roles_to_add:
        params = {'DBInstanceIdentifier': instance_id,
                  'RoleArn': role['role_arn'],
                  'FeatureName': role['feature_name']}
        _result, changed = call_method(client, module, method_name='add_role_to_db_instance', parameters=params)
    return changed

Anon7 - 2022
AnonSec Team