Ejemplo n.º 1
0
def test_get_regions_ignore_instances():
    """Test for method of the same name."""
    found_instances = utils.get_regions(must_contain_instances=False)
    expected_regions = ['eu-west-1', 'sa-east-1', 'us-east-1',
                        'ap-northeast-1', 'us-west-2', 'us-west-1']
    for expected_region in expected_regions:
        assert expected_region in found_instances
Ejemplo n.º 2
0
def test_get_regions_with_instances():
    """Test for method of the same name."""
    client = boto3.client('ec2', region_name='us-west-2')

    # toss an instance in us-west-2
    client.run_instances(ImageId='ami-123abc', MinCount=1, MaxCount=5)

    # be sure we get us-west-2 *only*
    assert ['us-west-2'] == utils.get_regions(must_contain_instances=True)
Ejemplo n.º 3
0
def test_get_regions_with_instances_or_snapshots():
    """Test for method of the same name."""
    client_uswest2 = boto3.client('ec2', region_name='us-west-2')

    # toss an instance in us-west-2
    client_uswest2.run_instances(ImageId='ami-123abc', MinCount=1, MaxCount=5)

    # be sure we get us-west-2 *only*
    assert ['us-west-2'] == utils.get_regions(must_contain_instances=True,
                                              must_contain_snapshots=False)

    # now say we don't filter by instances, be sure we get a lot of regions
    found_regions = utils.get_regions(must_contain_instances=False,
                                      must_contain_snapshots=False)
    expected_regions = [
        'eu-west-1', 'sa-east-1', 'us-east-1', 'ap-northeast-1', 'us-west-2',
        'us-west-1'
    ]
    for expected_region in expected_regions:
        assert expected_region in found_regions

    # now take a snapshot
    client_uswest1 = boto3.client('ec2', region_name='us-west-1')
    volume_results = client_uswest1.create_volume(
        Size=100, AvailabilityZone='us-west-1a')
    client_uswest1.create_snapshot(VolumeId=volume_results['VolumeId'])

    # be sure that snapshot filter works and only returns the snapshot region
    assert ['us-west-1'] == utils.get_regions(must_contain_instances=False,
                                              must_contain_snapshots=True)

    # now filter by both, should be nothing returned
    found_regions = utils.get_regions(must_contain_instances=True,
                                      must_contain_snapshots=True)
    assert len(found_regions) == 0

    # now snap in us-west-2 where we have an instance as well
    volume_results2 = client_uswest2.create_volume(
        Size=100, AvailabilityZone='us-west-2a')
    client_uswest2.create_snapshot(VolumeId=volume_results2['VolumeId'])
    found_regions = utils.get_regions(must_contain_instances=True,
                                      must_contain_snapshots=True)
    assert found_regions == ['us-west-2']
Ejemplo n.º 4
0
def perform_fanout_all_regions(context, cli=False):
    """For every region, run the supplied function"""
    # get regions, regardless of instances
    sns_topic = utils.get_topic_arn('CleanSnapshotTopic')
    LOG.debug('perform_fanout_all_regions using SNS topic %s', sns_topic)

    regions = utils.get_regions(must_contain_instances=True)
    for region in regions:
        sleep(5)  # API limit relief
        send_fanout_message(context,
                            region=region,
                            topic_arn=sns_topic,
                            cli=cli)

    LOG.info('Function clean_perform_fanout_all_regions completed')
Ejemplo n.º 5
0
def perform_fanout_all_regions(context, cli=False):
    """For every region, send a message (lambda) or run replication (cli)"""

    sns_topic = utils.get_topic_arn('ReplicationSnapshotTopic')
    LOG.debug('perform_fanout_all_regions using SNS topic %s', sns_topic)

    # get regions with instances running or stopped
    regions = utils.get_regions(must_contain_snapshots=True)
    for region in regions:
        sleep(5)  # API rate limiting help

        send_fanout_message(context=context,
                            region=region,
                            sns_topic=sns_topic,
                            cli=cli)
Ejemplo n.º 6
0
def perform_fanout_all_regions(context, cli=False):
    """For every region, run the supplied function"""

    sns_topic = utils.get_topic_arn('CreateSnapshotTopic')
    LOG.debug('perform_fanout_all_regions using SNS topic %s', sns_topic)

    # get regions with instances running or stopped
    regions = utils.get_regions(must_contain_instances=True)
    for region in regions:
        sleep(5)  # API rate limiting help

        send_fanout_message(context=context,
                            region=region,
                            sns_topic=sns_topic,
                            cli=cli)
Ejemplo n.º 7
0
def perform_fanout_all_regions(context, cli=False, installed_region='us-east-1'):
    """For every region, send a message (lambda) or run snapshots (cli)"""

    sns_topic = utils.get_topic_arn('CreateSnapshotTopic')
    LOG.debug('perform_fanout_all_regions using SNS topic %s', sns_topic)

    # configure replication based on extant configs for snapshots
    if type(context) is not MockContext:  # don't do in unit tests
        ensure_cloudwatch_rule_for_replication(context, installed_region)

    # get regions with instances running or stopped
    regions = utils.get_regions(must_contain_instances=True)
    for region in regions:
        sleep(5)  # API rate limiting help

        send_fanout_message(
            context=context,
            region=region,
            sns_topic=sns_topic,
            cli=cli)
Ejemplo n.º 8
0
def test_perform_fanout_all_regions_clean(mocker):
    """Test for method of the same name."""
    mocks.create_sns_topic('CleanSnapshotTopic')
    mocks.create_dynamodb()

    expected_regions = utils.get_regions()
    for r in expected_regions:  # must have an instance in the region to clean it
        mocks.create_instances(region=r)
    expected_sns_topic = utils.get_topic_arn('CleanSnapshotTopic', 'us-east-1')

    ctx = utils.MockContext()
    mocker.patch('ebs_snapper.clean.send_fanout_message')

    # fan out, and be sure we touched every region
    clean.perform_fanout_all_regions(ctx)
    for r in expected_regions:
        clean.send_fanout_message.assert_any_call(  # pylint: disable=E1103
            ctx,
            cli=False,
            region=r,
            topic_arn=expected_sns_topic)
Ejemplo n.º 9
0
def sanity_check(context, installed_region='us-east-1', aws_account_id=None):
    """Retrieve configuration from DynamoDB and return array of dictionary objects"""
    findings = []

    # determine aws account id
    if aws_account_id is None:
        found_owners = utils.get_owner_id(context)
    else:
        found_owners = [aws_account_id]

    if len(found_owners) <= 0:
        findings.append(
            'There are no instances I could find on this account. ' +
            'Cannot figure out the account number without any instances. ' +
            'Without account number, cannot figure out what to name the S3 bucket or stack.'
        )
        return findings
    else:
        aws_account = found_owners[0]

    # The bucket does not exist or you have no access
    bucket_exists = None
    try:
        s3_client = boto3.client('s3', region_name=installed_region)
        ebs_bucket_name = 'ebs-snapper-{}'.format(aws_account)
        s3_client.head_bucket(Bucket=ebs_bucket_name)
        bucket_exists = True
    except ClientError:
        bucket_exists = False

    # Configurations exist but tags do not
    configurations = []
    dynamodb_exists = None
    try:
        configurations = dynamo.list_configurations(context, installed_region)
        dynamodb_exists = True
    except ClientError:
        configurations = []
        dynamodb_exists = False

    # we're going across all regions, but store these in one
    regions = utils.get_regions(must_contain_instances=True)
    ignored_tag_values = ['false', '0', 'no']
    found_config_tag_values = []
    found_backup_tag_values = []

    # check out all the configs in dynamodb
    for config in configurations:
        # if it's missing the match section, ignore it
        if not utils.validate_snapshot_settings(config):
            findings.append(
                "Found a snapshot configuration that isn't valid: {}".format(
                    str(config)))
            continue

        # build a boto3 filter to describe instances with
        configuration_matches = config['match']
        filters = utils.convert_configurations_to_boto_filter(
            configuration_matches)
        for k, v in configuration_matches.iteritems():

            if str(v).lower() in ignored_tag_values:
                continue

            to_add = '{}, value:{}'.format(k, v)
            found_config_tag_values.append(to_add)

        # if we ended up with no boto3 filters, we bail so we don't snapshot everything
        if len(filters) <= 0:
            LOG.warn('Could not convert configuration match to a filter: %s',
                     configuration_matches)
            findings.append(
                "Found a snapshot configuration that couldn't be converted to a filter"
            )
            continue

        filters.append({
            'Name': 'instance-state-name',
            'Values': ['running', 'stopped']
        })

        found_instances = None
        for r in regions:
            ec2 = boto3.client('ec2', region_name=r)
            instances = ec2.describe_instances(Filters=filters)
            res_list = instances.get('Reservations', [])

            for reservation in res_list:
                inst_list = reservation.get('Instances', [])

                if len(inst_list) > 0:
                    found_instances = True
                    break

            # Look at all the tags on instances
            found_tag_data = ec2.describe_tags(Filters=[{
                'Name': 'resource-type',
                'Values': ['instance']
            }])

            for tag in found_tag_data.get('Tags', []):
                k = tag['Key']
                v = tag['Value']

                if str(v).lower() in ignored_tag_values:
                    continue

                to_add = 'tag:{}, value:{}'.format(k, v)
                if k.lower() in ['backup'
                                 ] and to_add not in found_backup_tag_values:
                    found_backup_tag_values.append(to_add)

        if not found_instances:
            long_config = []
            for k, v in configuration_matches.iteritems():
                long_config.append('{}, value:{}'.format(k, v))
            findings.append(
                "{} was configured, but didn't match any instances".format(
                    ", ".join(long_config)))

    if len(found_backup_tag_values) > 0 or len(found_config_tag_values) > 0:
        if not (bucket_exists and dynamodb_exists):
            findings.append(
                'Configuations or tags are present, but EBS snapper not fully deployed'
            )

    if bucket_exists and dynamodb_exists and len(configurations) == 0:
        findings.append(
            'No configurations existed for this account, but ebs-snapper was deployed'
        )

    # tagged instances without any config
    for s in found_backup_tag_values:
        if s not in found_config_tag_values:
            findings.append(
                '{} was tagged on an instance, but no configuration exists'.
                format(s))

    LOG.debug("configs: %s", str(found_config_tag_values))
    LOG.debug("tags: %s", str(found_backup_tag_values))

    return findings