Beispiel #1
0
def _stop_ec2_instances(awsclient, ec2_instances, wait=True):
    """Helper to stop ec2 instances.
    By default it waits for instances to stop.

    :param awsclient:
    :param ec2_instances:
    :param wait: waits for instances to stop
    :return:
    """
    if len(ec2_instances) == 0:
        return
    client_ec2 = awsclient.get_client('ec2')

    # get running instances
    running_instances = all_pages(
        client_ec2.describe_instance_status,
        {
            'InstanceIds': ec2_instances,
            'Filters': [{
                'Name': 'instance-state-name',
                'Values': ['pending', 'running']
            }]
        },
        lambda r: [i['InstanceId'] for i in r.get('InstanceStatuses', [])],
    )

    if running_instances:
        log.info('Stopping EC2 instances: %s', running_instances)
        client_ec2.stop_instances(InstanceIds=running_instances)

        if wait:
            # wait for instances to stop
            waiter_inst_stopped = client_ec2.get_waiter('instance_stopped')
            waiter_inst_stopped.wait(InstanceIds=running_instances)
Beispiel #2
0
def test_ec2_instance_stop_start(awsclient, simple_cloudformation_stack_with_ec2):
    def _get_instance_status(ec2_instance):
        # helper to check the status
        client_ec2 = awsclient.get_client('ec2')
        instances_status = all_pages(
            client_ec2.describe_instance_status,
            {
                'InstanceIds': [ec2_instance],
                'IncludeAllInstances': True
            },
            lambda r: [i['InstanceState']['Name'] for i in r.get('InstanceStatuses', [])],
        )[0]
        return instances_status

    stack_name = _get_stack_name(config_ec2_stack)
    client_cfn = awsclient.get_client('cloudformation')
    resources = all_pages(
        client_cfn.list_stack_resources,
        { 'StackName': stack_name },
        lambda r: r['StackResourceSummaries']
    )
    instances = [
        r['PhysicalResourceId'] for r in resources
        if r['ResourceType'] == 'AWS::EC2::Instance'
    ]
    assert _get_instance_status(instances[0]) == 'running'

    _stop_ec2_instances(awsclient, instances, wait=True)
    assert _get_instance_status(instances[0]) == 'stopped'

    _start_ec2_instances(awsclient, instances, wait=True)
    assert _get_instance_status(instances[0]) == 'running'
Beispiel #3
0
 def _get_instance_status(ec2_instance):
     # helper to check the status
     client_ec2 = awsclient.get_client('ec2')
     instances_status = all_pages(
         client_ec2.describe_instance_status,
         {
             'InstanceIds': [ec2_instance],
             'IncludeAllInstances': True
         },
         lambda r: [i['InstanceState']['Name'] for i in r.get('InstanceStatuses', [])],
     )[0]
     return instances_status
Beispiel #4
0
def test_all_pages_no_condition():
    state = {'counter': 0}

    def dummy_method(**kwargs):
        # I represent the service method
        nextToken = kwargs.pop('nextToken', None)
        if nextToken:
            assert nextToken == state['counter']
        if state['counter'] < 5:
            state['counter'] += 1
            kwargs['nextToken'] = state['counter']
        return kwargs

    actual = all_pages(dummy_method, {'foo': 'bar'},
                       lambda r: r['foo'] + str(r.get('nextToken', '')))
    assert actual == ['bar1', 'bar2', 'bar3', 'bar4', 'bar5', 'bar']
Beispiel #5
0
def _start_ec2_instances(awsclient, ec2_instances, wait=True):
    """Helper to start ec2 instances

    :param awsclient:
    :param ec2_instances:
    :param wait: waits for instances to start
    :return:
    """
    if len(ec2_instances) == 0:
        return
    client_ec2 = awsclient.get_client('ec2')

    # get stopped instances
    stopped_instances = all_pages(
        client_ec2.describe_instance_status,
        {
            'InstanceIds': ec2_instances,
            'Filters': [{
                'Name': 'instance-state-name',
                'Values': ['stopping', 'stopped']
            }],
            'IncludeAllInstances': True
        },
        lambda r: [i['InstanceId'] for i in r.get('InstanceStatuses', [])],
    )

    if stopped_instances:
        # start all stopped instances
        log.info('Starting EC2 instances: %s', stopped_instances)
        client_ec2.start_instances(InstanceIds=stopped_instances)

        if wait:
            # wait for instances to come up
            waiter_inst_running = client_ec2.get_waiter('instance_running')
            waiter_inst_running.wait(InstanceIds=stopped_instances)

            # wait for status checks
            waiter_status_ok = client_ec2.get_waiter('instance_status_ok')
            waiter_status_ok.wait(InstanceIds=stopped_instances)
Beispiel #6
0
def start_stack(awsclient, conf, use_suspend=False):
    """Start an existing stack on AWS cloud.

    :param awsclient:
    :param conf:
    :param use_suspend: use suspend and resume on the autoscaling group
    :return: exit_code
    """
    stack_name = _get_stack_name(conf)
    exit_code = 0

    # check for DisableStop
    disable_stop = conf.get('deployment', {}).get('DisableStop', False)
    if disable_stop:
        log.warn('\'DisableStop\' is set - nothing to do!')
    else:
        if not stack_exists(awsclient, stack_name):
            log.warn('Stack \'%s\' not deployed - nothing to do!', stack_name)
        else:
            client_cfn = awsclient.get_client('cloudformation')
            client_autoscaling = awsclient.get_client('autoscaling')
            client_rds = awsclient.get_client('rds')

            resources = all_pages(
                client_cfn.list_stack_resources,
                { 'StackName': stack_name },
                lambda r: r['StackResourceSummaries']
            )

            autoscaling_groups = [
                r for r in resources
                if r['ResourceType'] == 'AWS::AutoScaling::AutoScalingGroup'
            ]

            # lookup all types of scaling processes
            #    [Launch, Terminate, HealthCheck, ReplaceUnhealthy, AZRebalance
            #     AlarmNotification, ScheduledActions, AddToLoadBalancer]
            response = client_autoscaling.describe_scaling_process_types()
            scaling_process_types = [t['ProcessName'] for t in response.get('Processes', [])]

            # starting db instances
            db_instances = [
                r['PhysicalResourceId'] for r in resources
                if r['ResourceType'] == 'AWS::RDS::DBInstance'
            ]
            stopped_db_instances = _filter_db_instances_by_status(
                awsclient, db_instances, ['stopped']
            )
            for db in stopped_db_instances:
                log.info('Starting RDS instance \'%s\'', db)
                client_rds.start_db_instance(DBInstanceIdentifier=db)

            # wait for db instances to become available
            for db in stopped_db_instances:
                waiter_db_available = client_rds.get_waiter('db_instance_available')
                waiter_db_available.wait(DBInstanceIdentifier=db)

            # starting ec2 instances
            instances = [
                r['PhysicalResourceId'] for r in resources
                if r['ResourceType'] == 'AWS::EC2::Instance'
            ]
            _start_ec2_instances(awsclient, instances)

            if autoscaling_groups and not use_suspend:
                # get template and parameters from cloudformation
                response = client_cfn.get_template(
                    StackName=stack_name,
                    TemplateStage='Processed'
                )
                template = response.get('TemplateBody', {})
                response = client_cfn.describe_stacks(
                    StackName=stack_name
                )

                parameters = response['Stacks'][0].get('Parameters', {})

            for asg in autoscaling_groups:
                if use_suspend:
                    # alternative implementation to speed up start
                    # only problem is that instances must survive stop & start
                    # find instances in autoscaling group
                    instances = all_pages(
                        client_autoscaling.describe_auto_scaling_instances,
                        {},
                        lambda r: [i['InstanceId'] for i in r.get('AutoScalingInstances', [])
                                   if i['AutoScalingGroupName'] == asg['PhysicalResourceId']],
                    )
                    _start_ec2_instances(awsclient, instances)

                    # resume all autoscaling processes
                    log.info('Resuming all autoscaling processes for \'%s\'',
                             asg['LogicalResourceId'])
                    response = client_autoscaling.resume_processes(
                        AutoScalingGroupName=asg['PhysicalResourceId'],
                        ScalingProcesses=scaling_process_types
                    )
                else:
                    # resize autoscaling group back to its original values
                    log.info('Resize autoscaling group \'%s\' back to original values',
                             asg['LogicalResourceId'])
                    min, max = _get_autoscaling_min_max(
                        template, parameters, asg['LogicalResourceId'])
                    response = client_autoscaling.update_auto_scaling_group(
                        AutoScalingGroupName=asg['PhysicalResourceId'],
                        MinSize=min,
                        MaxSize=max
                    )

    return exit_code
Beispiel #7
0
def stop_stack(awsclient, conf, use_suspend=False):
    """Stop an existing stack on AWS cloud.

    :param awsclient:
    :param conf:
    :param use_suspend: use suspend and resume on the autoscaling group
    :return: exit_code
    """
    stack_name = _get_stack_name(conf)
    exit_code = 0

    # check for DisableStop
    disable_stop = conf.get('deployment', {}).get('DisableStop', False)
    if disable_stop:
        log.warn('\'DisableStop\' is set - nothing to do!')
    else:
        if not stack_exists(awsclient, stack_name):
            log.warn('Stack \'%s\' not deployed - nothing to do!', stack_name)
        else:
            client_cfn = awsclient.get_client('cloudformation')
            client_autoscaling = awsclient.get_client('autoscaling')
            client_rds = awsclient.get_client('rds')
            client_ec2 = awsclient.get_client('ec2')

            resources = all_pages(
                client_cfn.list_stack_resources,
                { 'StackName': stack_name },
                lambda r: r['StackResourceSummaries']
            )

            autoscaling_groups = [
                r for r in resources
                if r['ResourceType'] == 'AWS::AutoScaling::AutoScalingGroup'
            ]

            # lookup all types of scaling processes
            #    [Launch, Terminate, HealthCheck, ReplaceUnhealthy, AZRebalance
            #     AlarmNotification, ScheduledActions, AddToLoadBalancer]
            response = client_autoscaling.describe_scaling_process_types()
            scaling_process_types = [t['ProcessName'] for t in response.get('Processes', [])]

            for asg in autoscaling_groups:
                # find instances in autoscaling group
                ec2_instances = all_pages(
                    client_autoscaling.describe_auto_scaling_instances,
                    {},
                    lambda r: [i['InstanceId'] for i in r.get('AutoScalingInstances', [])
                               if i['AutoScalingGroupName'] == asg['PhysicalResourceId']],
                )

                if use_suspend:
                    # alternative implementation to speed up start
                    # only problem is that instances must survive stop & start
                    # suspend all autoscaling processes
                    log.info('Suspending all autoscaling processes for \'%s\'',
                             asg['LogicalResourceId'])
                    response = client_autoscaling.suspend_processes(
                        AutoScalingGroupName=asg['PhysicalResourceId'],
                        ScalingProcesses=scaling_process_types
                    )

                    _stop_ec2_instances(awsclient, ec2_instances)
                else:
                    running_instances = all_pages(
                        client_ec2.describe_instance_status,
                        {
                            'InstanceIds': ec2_instances,
                            'Filters': [{
                                'Name': 'instance-state-name',
                                'Values': ['pending', 'running']
                            }]
                        },
                        lambda r: [i['InstanceId'] for i in r.get('InstanceStatuses', [])],
                    )
                    # resize autoscaling group (min, max = 0)
                    log.info('Resize autoscaling group \'%s\' to minSize=0, maxSize=0',
                             asg['LogicalResourceId'])
                    response = client_autoscaling.update_auto_scaling_group(
                        AutoScalingGroupName=asg['PhysicalResourceId'],
                        MinSize=0,
                        MaxSize=0
                    )
                    if running_instances:
                        # wait for instances to terminate
                        waiter_inst_terminated = client_ec2.get_waiter('instance_terminated')
                        waiter_inst_terminated.wait(InstanceIds=running_instances)

            # stopping ec2 instances
            instances = [
                r['PhysicalResourceId'] for r in resources
                if r['ResourceType'] == 'AWS::EC2::Instance'
            ]
            _stop_ec2_instances(awsclient, instances)

            # stopping db instances
            db_instances = [
                r['PhysicalResourceId'] for r in resources
                if r['ResourceType'] == 'AWS::RDS::DBInstance'
            ]
            running_db_instances = _filter_db_instances_by_status(
                awsclient, db_instances, ['available']
            )
            for db in running_db_instances:
                log.info('Stopping RDS instance \'%s\'', db)
                client_rds.stop_db_instance(DBInstanceIdentifier=db)

    return exit_code