'Trim excess snapshots, based on when they were taken. More current
snapshots are retained, with the number retained decreasing as you
move back in time.
If ebs volumes have a \'Name\' tag with a value, their snapshots
will be assigned the same tag when they are created. The values
of the \'Name\' tags for snapshots are used by this function to
group snapshots taken from the same volume (or from a series
of like-named volumes over time) for trimming.
For every group of like-named snapshots, this function retains
the newest and oldest snapshots, as well as, by default,  the
first snapshots taken in each of the last eight hours, the first
snapshots taken in each of the last seven days, the first snapshots
taken in the last 4 weeks (counting Midnight Sunday morning as
the start of the week), and the first snapshot from the first
day of each month forever.
:type hourly_backups: int
:param hourly_backups: How many recent hourly backups should be saved.
:type daily_backups: int
:param daily_backups: How many recent daily backups should be saved.
:type weekly_backups: int
:param weekly_backups: How many recent weekly backups should be saved.
:type monthly_backups: int
:param monthly_backups: How many monthly backups should be saved. Use True for no limit.'

def trim_snapshots(self, hourly_backups=8, daily_backups=7, weekly_backups=4, monthly_backups=True):

    now = datetime.utcnow()
    last_hour = datetime(now.year, now.month, now.day, now.hour)
    last_midnight = datetime(now.year, now.month, now.day)
    last_sunday = (datetime(now.year, now.month, now.day) - timedelta(days=((now.weekday() + 1) % 7)))
    start_of_month = datetime(now.year, now.month, 1)
    target_backup_times = []
    oldest_snapshot_date = datetime(2007, 1, 1)
    for hour in range(0, hourly_backups):
        target_backup_times.append((last_hour - timedelta(hours=hour)))
    for day in range(0, daily_backups):
        target_backup_times.append((last_midnight - timedelta(days=day)))
    for week in range(0, weekly_backups):
        target_backup_times.append((last_sunday - timedelta(weeks=week)))
    one_day = timedelta(days=1)
    monthly_snapshots_added = 0
    while ((start_of_month > oldest_snapshot_date) and ((monthly_backups is True) or (monthly_snapshots_added < monthly_backups))):
        target_backup_times.append(start_of_month)
        monthly_snapshots_added += 1
        start_of_month -= one_day
        start_of_month = datetime(start_of_month.year, start_of_month.month, 1)
    temp = []
    for t in target_backup_times:
        if (temp.__contains__(t) == False):
            temp.append(t)
    target_backup_times = sorted(temp)
    all_snapshots = self.get_all_snapshots(owner='self')
    all_snapshots.sort(key=(lambda x: x.start_time))
    snaps_for_each_volume = {}
    for snap in all_snapshots:
        volume_name = snap.tags.get('Name')
        if volume_name:
            snaps_for_volume = snaps_for_each_volume.get(volume_name)
            if (not snaps_for_volume):
                snaps_for_volume = []
                snaps_for_each_volume[volume_name] = snaps_for_volume
            snaps_for_volume.append(snap)
    for volume_name in snaps_for_each_volume:
        snaps = snaps_for_each_volume[volume_name]
        snaps = snaps[:(-1)]
        time_period_number = 0
        snap_found_for_this_time_period = False
        for snap in snaps:
            check_this_snap = True
            while (check_this_snap and (time_period_number < target_backup_times.__len__())):
                snap_date = datetime.strptime(snap.start_time, '%Y-%m-%dT%H:%M:%S.000Z')
                if (snap_date < target_backup_times[time_period_number]):
                    if (snap_found_for_this_time_period == True):
                        if (not snap.tags.get('preserve_snapshot')):
                            try:
                                self.delete_snapshot(snap.id)
                                boto.log.info(('Trimmed    snapshot    %s    (%s)' % (snap.tags['Name'], snap.start_time)))
                            except EC2ResponseError:
                                boto.log.error(('Attempt    to    trim    snapshot    %s    (%s)    failed.    Possible    result    of    a    race    condition    with    trimming    on    another    server?' % (snap.tags['Name'], snap.start_time)))
                    else:
                        snap_found_for_this_time_period = True
                    check_this_snap = False
                else:
                    time_period_number += 1
                    snap_found_for_this_time_period = False
