Ejemplo n.º 1
0
def test_parse_snapshot_setting_crontab():
    """Test for method of the same name"""
    snapshot_settings = {
        'snapshot': {
            'minimum': 5,
            'frequency': '30 * * * *',
            'retention': '5 days'
        },
        'match': {
            'tag:backup': 'yes'
        }
    }
    retention, frequency = utils.parse_snapshot_settings(snapshot_settings)
    assert retention == timedelta(5)  # 5 days

    # generate some dates and times that we'll use to check crontab
    eleven_twentyfive = datetime(2011, 7, 17, 11, 25)
    eleven_thirtyfive = datetime(2011, 7, 17, 11, 35)

    offset_soon = frequency.next(eleven_twentyfive, default_utc=True)

    # 5 minutes from eleven_twentyfive
    assert timedelta(seconds=offset_soon) == timedelta(seconds=300)

    offset_later = frequency.next(eleven_thirtyfive, default_utc=True)
    # 5 minutes from eleven_thirtyfive
    assert timedelta(seconds=offset_later) == timedelta(seconds=3300)
Ejemplo n.º 2
0
def store_configuration(installed_region, object_id, aws_account_id,
                        configuration):
    """Function to store configuration item"""
    dynamodb = boto3.resource('dynamodb', region_name=installed_region)
    table = dynamodb.Table('ebs_snapshot_configuration')

    # be sure they parse correctly before we go saving them
    utils.parse_snapshot_settings(configuration)

    response = table.put_item(
        Item={
            'aws_account_id': aws_account_id,
            'id': object_id,
            'configuration': json.dumps(configuration)
        })

    return response.get('Attributes', {})
Ejemplo n.º 3
0
def test_parse_snapshot_setting_timedelta():
    """Test for method of the same name"""
    snapshot_settings = {
        'snapshot': {'minimum': 5, 'frequency': '2 hours', 'retention': '5 days'},
        'match': {'tag:backup': 'yes'}
    }
    retention, frequency = utils.parse_snapshot_settings(snapshot_settings)

    assert retention == timedelta(5)  # 5 days
    assert frequency == timedelta(0, 7200)  # 2 hours
Ejemplo n.º 4
0
def test_perform_snapshot_skipped(mocker):
    """Test for method of the same name."""
    # some default settings for this test
    region = 'us-west-2'
    snapshot_settings = {
        'snapshot': {
            'minimum': 5,
            'frequency': '2 hours',
            'retention': '5 days'
        },
        'match': {
            'tag:backup': 'yes'
        }
    }
    mocks.create_dynamodb('us-east-1')
    dynamo.store_configuration('us-east-1', 'some_unique_id', '111122223333',
                               snapshot_settings)

    # create an instance and record the id
    instance_id = mocks.create_instances(region, count=1)[0]

    # figure out the EBS volume that came with our instance
    instance_details = utils.get_instance(instance_id, region)
    block_devices = instance_details.get('BlockDeviceMappings', [])
    volume_id = block_devices[0]['Ebs']['VolumeId']

    # determine what we should be tagging the snapshot
    ret, freq = utils.parse_snapshot_settings(snapshot_settings)  # pylint: disable=unused-variable
    now = datetime.datetime.now(dateutil.tz.tzutc())
    delete_on_dt = now + ret
    delete_on = delete_on_dt.strftime('%Y-%m-%d')

    # now take a snapshot, so we expect this next one to be skipped
    utils.snapshot_and_tag(instance_id, 'ami-123abc', volume_id, delete_on,
                           region)

    # patch the final method that takes a snapshot
    mocker.patch('ebs_snapper.utils.snapshot_and_tag')

    # since there are no snapshots, we should expect this to trigger one
    ctx = utils.MockContext()
    snapshot.perform_snapshot(ctx, region)

    # test results (should not create a second snapshot)
    utils.snapshot_and_tag.assert_not_called()  # pylint: disable=E1103
Ejemplo n.º 5
0
def perform_snapshot(context, region, installed_region='us-east-1'):
    """Check the region and instance, and see if we should take any snapshots"""
    LOG.info('Reviewing snapshots in region %s', region)

    # fetch these, in case we need to figure out what applies to an instance
    configurations = dynamo.list_configurations(context, installed_region)
    LOG.debug('Fetched all possible configuration rules from DynamoDB')

    # build a list of any IDs (anywhere) that we should ignore
    ignore_ids = utils.build_ignore_list(configurations)

    # setup some lookup tables
    cache_data = utils.build_cache_maps(context, configurations, region, installed_region)
    all_instances = cache_data['instance_id_to_data']
    instance_configs = cache_data['instance_id_to_config']
    volume_snap_recent = cache_data['volume_id_to_most_recent_snapshot_date']

    for instance_id in set(all_instances.keys()):
        # before we go do some work
        if timeout_check(context, 'perform_snapshot'):
            break

        if instance_id in ignore_ids:
            continue

        snapshot_settings = instance_configs[instance_id]

        # parse out snapshot settings
        retention, frequency = utils.parse_snapshot_settings(snapshot_settings)

        # grab the data about this instance id, if we don't already have it
        instance_data = all_instances[instance_id]

        ami_id = instance_data['ImageId']
        LOG.info('Reviewing snapshots in region %s on instance %s', region, instance_id)

        for dev in instance_data.get('BlockDeviceMappings', []):
            # before we go make a bunch more API calls
            if timeout_check(context, 'perform_snapshot'):
                break

            # we probably should have been using volume keys from one of the
            # caches here, but since we're not, we're going to have to check here too
            LOG.debug('Considering device %s', dev)
            volume_id = dev['Ebs']['VolumeId']

            if volume_id in ignore_ids:
                continue

            # find snapshots
            recent = volume_snap_recent.get(volume_id)
            now = datetime.datetime.now(dateutil.tz.tzutc())

            # snapshot due?
            if should_perform_snapshot(frequency, now, volume_id, recent):
                LOG.debug('Performing snapshot for %s, calculating tags', volume_id)
            else:
                LOG.debug('NOT Performing snapshot for %s', volume_id)
                continue

            # perform actual snapshot and create tag: retention + now() as a Y-M-D
            delete_on_dt = now + retention
            delete_on = delete_on_dt.strftime('%Y-%m-%d')

            volume_data = utils.get_volume(volume_id, region=region)
            expected_tags = utils.calculate_relevant_tags(
                instance_data.get('Tags', None),
                volume_data.get('Tags', None))

            utils.snapshot_and_tag(
                instance_id,
                ami_id,
                volume_id,
                delete_on,
                region,
                additional_tags=expected_tags)
Ejemplo n.º 6
0
def test_perform_snapshot(mocker):
    """Test for method of the same name."""
    # some default settings for this test
    region = 'us-west-2'

    snapshot_settings = {
        'snapshot': {'minimum': 5, 'frequency': '2 hours', 'retention': '5 days'},
        'match': {'tag:backup': 'yes'}
    }

    # create an instance and record the id
    instance_id = mocks.create_instances(region, count=1)[0]

    # need to filter instances, so need dynamodb present
    mocks.create_dynamodb('us-east-1')
    dynamo.store_configuration('us-east-1', 'some_unique_id', '111122223333', snapshot_settings)

    # figure out the EBS volume that came with our instance
    instance_details = utils.get_instance(instance_id, region)
    block_devices = instance_details.get('BlockDeviceMappings', [])
    volume_id = block_devices[0]['Ebs']['VolumeId']

    # determine what we should be tagging the snapshot
    ret, freq = utils.parse_snapshot_settings(snapshot_settings)  # pylint: disable=unused-variable
    now = datetime.datetime.now(dateutil.tz.tzutc())
    delete_on_dt = now + ret
    delete_on = delete_on_dt.strftime('%Y-%m-%d')

    # apply some tags
    client = boto3.client('ec2', region_name=region)
    instance_tags = [
        {'Key': 'Name', 'Value': 'Foo'},
        {'Key': 'Service', 'Value': 'Bar'},
        {'Key': 'backup', 'Value': 'yes'},
    ]
    client.create_tags(DryRun=False, Resources=[instance_id], Tags=instance_tags)

    # override one of the tags
    volume_tags = [{'Key': 'Service', 'Value': 'Baz'}]
    client.create_tags(DryRun=False, Resources=[volume_id], Tags=volume_tags)

    # when combined, we expect tags to be this.
    tags = [
        {'Key': 'Name', 'Value': 'Foo'},
        {'Key': 'Service', 'Value': 'Baz'},
        {'Key': 'backup', 'Value': 'yes'},
    ]

    # patch the final method that takes a snapshot
    mocker.patch('ebs_snapper.utils.snapshot_and_tag')

    # since there are no snapshots, we should expect this to trigger one
    ctx = utils.MockContext()
    snapshot.perform_snapshot(ctx, region)

    # test results
    utils.snapshot_and_tag.assert_any_call(  # pylint: disable=E1103
        instance_id,
        'ami-123abc',
        volume_id,
        delete_on,
        region,
        additional_tags=tags)