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)
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', {})
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
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
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)
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)