Search This Blog

Thursday 31 December 2015

AWS Snapshot monitoring

So I have a bunch of AWS EC2 instances running that need regular backup.  The majority of these instances have specific backups running for specific data, but given the low cost of snapshot storage I figured that I could take a regular snapshot in addition and give myself an additional level of baseline assurance that my data is (relatively) safe.  All features of AWS are available through various APIs (I used Python Boto here, but most mainstream languages have an equivalent), so it's a relatively straightforward task to query the snapshot state of my instances and report any issues.

I created a process that automates the snapshoting of various EBS volumes which works by looking for a couple of tags on instances, but then wanted to report on the status of this process each day.  The script below loads the information from AWS and then loops through all of my volumes in the region.  For each volume it checks for tag that I defined exists and what the most recent snapshot is, although all other volumes are also included whether they are tagged, or even attached to an instance, and added to the report.

You will notice that I have not included any access_key parameters anywhere - this is because I completed the AWS Configuration on the host computer before hand - http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html



from boto.ec2.connection import EC2Connection
import boto
from dateutil import parser
import boto.ses
import datetime
import pytz


#check to see whether volume has snapshot tags attached to it
def volumeIsTaggedForSnapshots(v):
    if 'AutomatedEBSSnapshots' in v.tags:
        if v.tags['AutomatedEBSSnapshots'] == 'daily':
            return True
    return False


#find date of most recent snapshot for volume
def lastSnapshotDateForVolume(v):
    snapshot_dates = []
    for s in snaps:
        if v.id == s.volume_id:
            snapshot_dates.append(parser.parse(s.start_time))
    if len(snapshot_dates) == 0:
        return pytz.utc.localize(datetime.datetime.min)
    else:
        return max(snapshot_dates)


#find name of instance that volume is attached to
def getInstanceNameForVolume(v):
    for res in reservations:
        for ec2 in res.instances:
            if ec2.id == v.attach_data.instance_id:
                if 'Name' in ec2.tags:
                    return ec2.tags['Name']
                else:
                    return ec2.id
    return 'UNKNOWN'


#create and email report
def sendEmail(vInfos):

    emailTemplate = open('emailTemplate.html', 'r').read()
    rowTemplate = open('rowTemplate.html').read()

    messageBody = ''

    myList = sorted(vInfos, cmp=lambda x,y: cmp(x['lastSnapshotDate'], y['lastSnapshotDate'] ))

    for vi in myList:
        thisRow = rowTemplate
        thisRow = thisRow.replace('{id}', vi['id'])
        thisRow = thisRow.replace('{name}', vi['name'])
        thisRow = thisRow.replace('{tagged}', str(vi['taggedForSnapshot']))

        if str(vi['lastSnapshotDate']) == 'UNKNOWN':
            thisRow = thisRow.replace('{lastSnap}', 'UNKNOWN')
            thisRow = thisRow.replace('{lastSnap_class}', 'tdOrange')
        else:
            lastSnapDelta = datetime.datetime.now(tz=pytz.UTC) - vi['lastSnapshotDate']
            lastSnapDesc = '{0} ({1} days ago)'.format(str(vi['lastSnapshotDate']), str(lastSnapDelta.days))
            thisRow = thisRow.replace('{lastSnap}', lastSnapDesc)
            if lastSnapDelta.seconds > 86400:
                thisRow = thisRow.replace('{lastSnap_class}', 'tdRed')
            else:
                thisRow = thisRow.replace('{lastSnap_class}', 'tdGreen')

        thisRow = thisRow.replace('{instance}', str(vi['instanceID']))
        messageBody = messageBody + thisRow

    messageBody = emailTemplate.replace('{volsContent}', messageBody)

    emailconn = boto.ses.connect_to_region("eu-west-1")
    return emailconn.send_email('from@mydomain.com', 'Volume snapshot report', '', ['me@mydomain.com', 'someone.else@mydomain.com'], html_body=messageBody)



#######################################################

#open connection to AWS
conn = boto.ec2.connect_to_region("eu-west-1")

#load reservations (ec2 instances), volumes and snapshot collections into memory
reservations = conn.get_all_reservations()
vols = conn.get_all_volumes()
snaps = conn.get_all_snapshots(owner='self')

#define empty list for processed information
volsInfo = []

#loop all volumes and build a list of procesed volume information
for v in vols:
    volInfo = {'id' : v.id}
    if 'Name' in v.tags:
        volInfo['name'] = v.tags['Name']
    volInfo['taggedForSnapshot'] = volumeIsTaggedForSnapshots(v)
    volInfo['lastSnapshotDate'] = lastSnapshotDateForVolume(v)
    volInfo['instanceID'] = getInstanceNameForVolume(v)
    volsInfo.append(volInfo)

#do something with the output
print sendEmail(volsInfo)






OTHER FILES:


emailTemplate.html

<html>
<style>
    table {border-collapse: collapse; border: solid 1px black; }
    td {border-collapse: collapse; border: solid 1px black; padding: 3px;}
    .tdGreen{background: lime}
    .tdRed{background: red}
    .tdOrange{background: orange}
</style>
<body>
<table>
    <tr>
        <td>Volume ID</td>
        <td>Volume Name</td>
        <td>Tagged for snapshot</td>
        <td>Last snapshot date</td>
        <td>Instance ID</td>
    </tr>
    {volsContent}
</table>
</body>
</html>

rowTemplate.html

<tr>
<td class="{id_class}">{id}</td>
<td class="{name_class}">{name}</td>
<td class="{tagged_class}">{tagged}</td>
<td class="{lastSnap_class}">{lastSnap}</td>
<td class="{instance_class}">{instance}</td>
</tr>

No comments:

Post a Comment