public
Last active — forked from noneal/aws_usage.py

A script to query the Amazon Web Services (S3/EC2/etc) usage reports programmatically.

  • Download Gist
aws_usage.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
#!/usr/bin/env python
 
"""
A script to query the Amazon Web Services usage reports programmatically.
 
Ideally this wouldn't exist, and Amazon would provide an API we can use
instead, but hey - that's life.
 
Basically takes your AWS account username and password, logs into the
website as you, and grabs the data out. Always gets the 'All Usage Types'
report for the specified service.
 
Requirements:
 
* Mechanize: http://wwwsearch.sourceforge.net/mechanize/
You can install this via pip/easy_install
 
Run with -h to see the available options.
"""
 
import re
import os
import sys
from datetime import date
import time
 
import mechanize
 
FORMATS = ('xml', 'csv')
PERIODS = ('hours', 'days', 'months')
SERVICES = ('AmazonS3', 'AmazonEC2', 'AmazonCloudFront', 'AmazonSimpleDB', 'AWSQueueService', 'IngestionService', 'AmazonVPC',)
 
ACCOUNT_SUMMARY_URL = "https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=activity-summary"
FORM_URL = "https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=usage-report"
 
def get_current(username, password, debug=False):
br = mechanize.Browser()
br.set_handle_robots(False)
 
if debug:
# Log information about HTTP redirects and Refreshes.
br.set_debug_redirects(True)
# Log HTTP response bodies (ie. the HTML, most of the time).
br.set_debug_responses(True)
# Print HTTP headers.
br.set_debug_http(True)
br.addheaders = [
# the login process 404s if you leave Python's UA string
('User-Agent', 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-us) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4'),
('Accept', 'text/html, application/xml, */*'),
]
# login
print >>sys.stderr, "Logging in..."
try:
resp = br.open(ACCOUNT_SUMMARY_URL)
#Some funkiness in DOCTYPE string. mechanize doesn't like
#results in: mechanize._form.ParseError: unexpected '\\' char in declaration
#if we don't strip out
resp.set_data(re.sub('<!DOCTYPE(.*)>', '', resp.get_data()))
br.set_response(resp)
br.select_form(name="signIn")
br["email"] = username
br["password"] = password
resp = br.submit() # submit current form
except Exception, e:
print >>sys.stderr, "Error logging in to AWS"
raise
 
cost = re.findall('Total Charges due on.*\$([0-9\,]+\.[0-9][0-9])', resp.get_data(), re.DOTALL)
if len(cost) > 0:
print "Total Charges due: %s" % cost[0]
else:
print "Current Charges Unknown"
 
 
def get_report(service, date_from, date_to, username, password, format='csv', period='days', debug=False):
br = mechanize.Browser()
br.set_handle_robots(False)
 
if debug:
# Log information about HTTP redirects and Refreshes.
br.set_debug_redirects(True)
# Log HTTP response bodies (ie. the HTML, most of the time).
br.set_debug_responses(True)
# Print HTTP headers.
br.set_debug_http(True)
br.addheaders = [
# the login process 404s if you leave Python's UA string
('User-Agent', 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1) Gecko/20090701 Ubuntu/9.04 (jaunty) Firefox/3.5'),
('Accept', 'text/html, application/xml, */*'),
]
# login
print >>sys.stderr, "Logging in..."
try:
resp = br.open(FORM_URL)
#Some funkiness in DOCTYPE string. mechanize doesn't like
#results in: mechanize._form.ParseError: unexpected '\\' char in declaration
#if we don't strip out
resp.set_data(re.sub('<!DOCTYPE(.*)>', '', resp.get_data()))
br.set_response(resp)
br.select_form(name="signIn")
br["email"] = username
br["password"] = password
resp = br.submit() # submit current form
except Exception, e:
print >>sys.stderr, "Error logging in to AWS"
raise
# service selector
print >>sys.stderr, "Selecting service %s..." % service
br.select_form(name="usageReportForm")
br["productCode"] = [service]
resp = br.submit()
# report selector
print >>sys.stderr, "Building report..."
br.select_form(name="usageReportForm")
# update timePeriod to fix: mechanize._form.ItemNotFoundError: insufficient items with name 'Custom date range'
br["timePeriod"] = ["aws-portal-custom-date-range"]
br["startYear"] = [str(date_from.year)]
br["startMonth"] = [str(date_from.month)]
br["startDay"] = [str(date_from.day)]
br["endYear"] = [str(date_to.year)]
br["endMonth"] = [str(date_to.month)]
br["endDay"] = [str(date_to.day)]
br["periodType"] = [period]
resp = br.submit("download-usage-report-%s" % format)
return resp.read()
if __name__ == "__main__":
from optparse import OptionParser
USAGE = (
"Usage: %prog [options] -s SERVICE DATE_FROM DATE_TO\n\n"
"DATE_FROM and DATE_TO should be in YYYY-MM-DD format (eg. 2009-01-31)\n"
"Username and Password can also be specified via AWS_USERNAME and AWS_PASSWORD environment variables.\n"
"\n"
"Available Services: " + ', '.join(SERVICES)
)
parser = OptionParser(usage=USAGE)
parser.add_option('-s', '--service', dest="service", type="choice", choices=SERVICES, help="The AWS service to query")
parser.add_option('-p', '--period', dest="period", type="choice", choices=PERIODS, default='days', metavar="PERIOD", help="Period of report entries")
parser.add_option('-f', '--format', dest="format", type="choice", choices=FORMATS, default='csv', metavar="FORMAT", help="Format of report")
parser.add_option('-U', '--username', dest="username", metavar="USERNAME", help="Email address for your AWS account")
parser.add_option('-P', '--password', dest="password", metavar="PASSWORD")
parser.add_option('-d', '--debug', action="store_true", dest="debug", default=False)
parser.add_option('-c', '--current', action="store_true", dest="current", default=False)
opts, args = parser.parse_args()
if not opts.username and not os.environ.get('AWS_USERNAME'):
parser.error("Must specify username option or set AWS_USERNAME")
if not opts.password and not os.environ.get('AWS_PASSWORD'):
parser.error("Must specify password option or set AWS_PASSWORD")
if opts.current:
kwopts = {
'username': opts.username or os.environ.get('AWS_USERNAME'),
'password': opts.password or os.environ.get('AWS_PASSWORD'),
'debug': opts.debug,
}
get_current(**kwopts)
else:
if len(args) < 2:
parser.error("Missing date range")
date_range = [date(*time.strptime(args[i], '%Y-%m-%d')[0:3]) for i in range(2)]
if date_range[1] < date_range[0]:
parser.error("End date < start date")
if not opts.service:
parser.error("Specify a service to query!")
kwopts = {
'service': opts.service,
'date_from': date_range[0],
'date_to': date_range[1],
'format': opts.format,
'period': opts.period,
'username': opts.username or os.environ.get('AWS_USERNAME'),
'password': opts.password or os.environ.get('AWS_PASSWORD'),
'debug': opts.debug,
}
print get_report(**kwopts)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.