Thursday, December 12, 2013

EC2 rotate snapshot


Here is the script to auto delete those snapshot that is older than n days

Requirements
- Python
- Boto >=2.4.0

option list

-k = access key ID
-s = secret access key
-a = how many days
-d = description contain
-r = region

example
ec2-rotate-snapshot.py -k AKIAert5EYQKJBN6TASQ -s 8ka5hp9lDOPjDNOJsdfnmaHcMWAsdamoWbH4HhBj -a 5 -d "backup" -r eu-west-1

this will check snapshot that is more than 5 day and contain word "backup" in region eu-west-1 and proceed to delete

reference/source
I get this from https://bitbucket.org/romabysen/ec2-rotate-snapshots/overview
owner is Lars Hansson
so please feel free to thanks him for this wonderful script




Script start here
--------------------------------------------------------------------------------

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# :copyright: (c) 2013 by Lars Hansson.
# :license: ISC, see LICENSE for more details.
#
"""Utility for rotating EC2 snaphots"""

import os
import sys
import time
import logging
from optparse import OptionParser
import datetime
import re
import boto.ec2

DEFAULT_REGION = boto.ec2.EC2Connection.DefaultRegionName


def setup_logging(level):
    logger = logging.getLogger('ec2-rotate-snapshot')
    ch = logging.StreamHandler()
    logger.setLevel(level)
    logger.addHandler(ch)


def log_err(msg):
    logger = logging.getLogger('ec2-rotate-snapshot')
    logger.error(msg)


def log_info(msg):
    logger = logging.getLogger('ec2-rotate-snapshot')
    logger.info(msg)


def match_tags(tags, m_tags):
    if not m_tags:
        return True
    for item in m_tags:
        if item.find('=') != -1:
            (tag, value) = item.split('=')
            for k, v in tags.items():
                if k == tag and v == value:
                    return True
        else:
            if item in tags.keys():
                return True
    return False


def match_timeframe(start_time, timeframe):
    if not timeframe:
        return True
    dt_start_time = datetime.datetime.strptime(start_time, '%Y-%m-%dT%H:%M:%S.%fZ')
    (start, end) = timeframe.split('-')
    frame_start = dt_start_time.replace(hour=int(start.split(':')[0]),
                                        minute=int(start.split(':')[1]),
                                        second=0)
    frame_end = dt_start_time.replace(hour=int(end.split(':')[0]),
                                      minute=int(end.split(':')[1]),
                                      second=0)
    if frame_end < frame_start:
        frame_end = frame_end + datetime.timedelta(days=+1)
    return frame_start <= dt_start_time <= frame_end


def match_age(start_time, age):
    if not age:
        return True
    st = time.strptime(start_time, '%Y-%m-%dT%H:%M:%S.%fZ')
    at = time.gmtime(time.time() - (86400 * age))
    return st < at


def match_string(mstring, pattern):
    if not pattern:
        return True
    if not mstring:
        return False
    if re.search(pattern, mstring) is not None:
        return True
    return False


def cleanup_snapshots(pattern, opts):
    conn = boto.ec2.connect_to_region(opts.region)
    for snap in conn.get_all_snapshots(owner='self'):
        if (match_string(snap.description, pattern) and match_tags(snap.tags, opts.tag) and
            match_timeframe(snap.start_time, opts.timeframe) and
                match_age(snap.start_time, opts.age)):
            if opts.delete:
                try:
                    conn.delete_snapshot(snap.id)
                except boto.exception.EC2ResponseError:
                    # Retry deleting since sometimes random errors happen
                    try:
                        time.sleep(2)
                        conn.delete_snapshot(snap.id)
                    except boto.exception.EC2ResponseError:
                        log_err('Error! Snapshot %s not deleted' % (snap.id))
                        continue
                log_info('Snapshot deleted: %s' % (snap.id))
                time.sleep(0.5)
            else:
                log_info('Snapshot: %s, start time: %s, description: %s, tags: %s' %
                        (snap.id, snap.start_time, snap.description, snap.tags.keys()))


def handle_options():
    """Parse commandline options"""
    usage = 'Usage: %prog [options] <regular expression>'
    version = '%prog 0.1.0'
    parser = OptionParser(usage=usage, version=version)
    parser.add_option('-k', dest='key', help='AWS access key id')
    parser.add_option('-s', dest='secret', help='AWS secret access key')
    parser.add_option('-r', dest='region', help='Region. Default: %s' % (DEFAULT_REGION))
    parser.add_option('-a', dest='age', type='int', default=None,
                      help='Older than these many days')
    parser.add_option('-t', action='append', dest='tag', default=None,
                      help='Match this tag and, optionally, content')
    parser.add_option('-T', dest='timeframe', default=None,
                      help='Timeframe selector. Example: 15:30-15:45')
    parser.add_option('-d', dest='delete', action='store_true', default=False,
                      help='Actually delete instead of printing the matches')
    parser.add_option('-q', action='store_true', default=False, dest='quiet', help='Only print errors')
    (opts, args) = parser.parse_args()
    if opts.quiet:
        setup_logging(logging.ERROR)
    else:
        setup_logging(logging.INFO)
    if opts.key is not None:
        os.environ["AWS_ACCESS_KEY_ID"] = opts.key
    if opts.secret is not None:
        os.environ["AWS_SECRET_ACCESS_KEY"] = opts.secret
    if len(args) == 0:
        log_err('A regular expression is required.')
        parser.print_usage()
        sys.exit(1)
    if len(args) > 1:
        log_err('Too many regular expressions.')
        parser.print_usage()
        sys.exit(1)
    return (opts, args)


def main():
    """The main loop"""
    (opts, args) = handle_options()
    pattern = args[0]
    cleanup_snapshots(pattern, opts)

if __name__ == '__main__':
    main()

No comments:

Post a Comment