-
Notifications
You must be signed in to change notification settings - Fork 0
/
sfaws.py
executable file
·132 lines (109 loc) · 4.15 KB
/
sfaws.py
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
#!/usr/bin/env python
import requests
import botocore
from boto3.session import Session
import collections
import re
from prettytable import PrettyTable
from datetime import datetime
def ec2_pricing_info():
"""
return ec2 pricing info obtained from github ec2info
screen scraped json
"""
if not hasattr(ec2_pricing_info, "ec2info"):
EC2INFO = ("https://raw.githubusercontent.com/powdahound/"
"ec2instances.info/master/www/instances.json")
ec2_pricing_info.ec2info =\
{r['instance_type']: r for r in requests.get(EC2INFO).json()}
return ec2_pricing_info.ec2info
def cloudFormation_templatename_classifier():
# cloud formation template generates these names
# If not using cloudformation templates, we need another matcher
cx = re.compile("(.*)-OpsManStack-(.*?)-.*")
def classifier(inst):
grpname = inst.security_groups[0]["GroupName"]
match = cx.match(grpname)
if match is not None:
return match.group(1), match.group(2)
else:
return None, None
return classifier
def ec2_servers_by_deployment(ec2, deployment_classfier, region):
"""
given ec2 connection and a deployment_classfier
return a dict of deployment --> instance info
"""
by_deployment = collections.defaultdict(list)
ec2info = ec2_pricing_info()
for inst in ec2.instances.filter(
Filters=[{'Name': 'instance-state-name', 'Values': ['running']}]):
deployment_name, deployment_id = deployment_classfier(inst)
if deployment_id is not None:
instance_info = {v['Key']: v['Value'] for v in inst.tags}
instance_info.update({
'type': inst.instance_type,
'deployment': deployment_name,
'launch_time': inst.launch_time,
'cost':
float(
ec2info[inst.instance_type]
['pricing'][region]['linux']['ondemand'])
})
by_deployment[deployment_id].append(instance_info)
return by_deployment
def summarize(by_deployment):
summ = {}
now = datetime.utcnow()
for dep, ilist in by_deployment.items():
total = 0
cnt = 0
mindt = now
for il in ilist:
cnt += 1
total += il["cost"]
mindt = min(mindt, il["launch_time"].replace(tzinfo=None))
total_cost = total * ((now - mindt).total_seconds()/3600.0)
summ[dep] = {"cnt": cnt, "total": total,
"deployment": il["deployment"],
"deployment_id": dep,
"launch_time": mindt,
"total_cost": int(total_cost)}
return summ
def report(summary):
pt = PrettyTable(['deployment', 'deploymentId',
'serverCount', 'costPerHour',
'runningSince', '$ totalSpend'],
padding=0, border=True, header=True)
for dep in summary.values():
pt.add_row((dep["deployment"], dep["deployment_id"],
dep["cnt"], dep["total"],
dep["launch_time"].strftime("%Y-%m-%d"),
dep["total_cost"]))
pt.align["$ totalSpend"] = "r"
print pt.get_string(sortby='$ totalSpend', reversesort=True)
def get_ec2_connection(profile, region):
session = Session(profile_name=profile, region_name=region)
return session.resource('ec2')
def get_args():
import argparse
argp = argparse.ArgumentParser()
argp.add_argument('--profile')
argp.add_argument('--region', default='us-east-1')
return argp
def main(argv):
args = get_args().parse_args(argv)
ec2 = get_ec2_connection(args.profile, args.region)
try:
report(summarize(ec2_servers_by_deployment(
ec2, cloudFormation_templatename_classifier(),
args.region)))
except botocore.exceptions.NoCredentialsError as ex:
print ex
print "Missing ~/.aws/credentials directory?"
print "http://boto3.readthedocs.org/en/latest/guide/configuration.html"
return -1
return 0
if __name__ == "__main__":
import sys
sys.exit(main(sys.argv[1:]))