19
19
import get_role_session
20
20
import datetime
21
21
import os
22
+ import sys
23
+ import traceback
24
+ import smtplib
25
+ from email .mime .multipart import MIMEMultipart
26
+ from email .mime .text import MIMEText
27
+ from email .mime .base import MIMEBase
28
+ from email import encoders
22
29
23
30
#load environment configurations and get values we need
24
31
role_name = os .environ .get ('ASSUMED_ROLE_NAME' , None )
28
35
report_filename = os .environ .get ('REPORT_FILE_NAME' )
29
36
now = datetime .datetime .now ().strftime ("%Y-%m-%dT%H:%M:%S-" )
30
37
38
+ #mail settings
39
+ sender = os .environ .get ('MAIL_SENDER' , None )
40
+ message = os .environ .get ('MAIL_MESSAGE' , None )
41
+ subject = os .environ .get ('MAIL_SUBJECT' , None )
42
+ mailhost = os .environ .get ('MAIL_HOST' , None )
43
+ port = os .environ .get ('MAIL_PORT' , None )
44
+ receivers = [x .strip () for x in os .environ .get ('MAIL_RECEIVERS' ).split (',' )]
45
+ reporting_limit = os .environ .get ('MAIL_REPORTING_LIMIT' , None )
46
+
47
+ def send_mail_attachment (message , attachment ):
48
+ msg = MIMEMultipart ()
49
+ # Create headers
50
+ msg ['Subject' ] = subject
51
+ msg ['From' ] = sender
52
+ msg ['To' ] = ";" .join (receivers )
53
+ part = MIMEBase ('application' , "octet-stream" )
54
+ # Attach attachment
55
+ part .set_payload (open (attachment , "rb" ).read ())
56
+ encoders .encode_base64 (part )
57
+ part .add_header ('Content-Disposition' , 'attachment' , filename = attachment .split ('/' )[- 1 ])
58
+ msg .attach (part )
59
+ msg .attach (MIMEText (message , 'plain' ))
60
+ # Send it off
61
+ smtpObj = smtplib .SMTP (mailhost , port )
62
+ smtpObj .sendmail (sender , receivers , msg .as_string ())
63
+ print ("Successfully sent email" )
64
+
65
+ def send_mail (message ):
66
+ msg = MIMEMultipart ()
67
+ # Create headers
68
+ msg ['Subject' ] = subject
69
+ msg ['From' ] = sender
70
+ msg ['To' ] = ";" .join (receivers )
71
+ # Attach message
72
+ msg .attach (MIMEText (message , 'plain' ))
73
+ # Send it off
74
+ smtpObj = smtplib .SMTP (mailhost , port )
75
+ smtpObj .sendmail (sender , receivers , msg .as_string ())
76
+ print ("Successfully sent email" )
77
+
31
78
#do some "formatting" of the limits collected by TA, as well as drop limits with no usage
32
79
def ta_limit_to_dict (limit ):
33
80
#extract values into dictionary
@@ -48,48 +95,69 @@ def ta_limit_to_dict(limit):
48
95
account_out = {'id' : account_id }
49
96
all_limits = []
50
97
#get boto3 session for the account
51
- sess = get_role_session .get_role_session (account_id , role_name , role_session_name )
52
- #instance_types = defaultdict(int)
53
- for region in regions :
54
- #get total EC2 instances since TA does not check TOTAL on-demand instances, only by instance type
55
- total_instances = 0
56
- ec2 = sess .resource ('ec2' , region_name = region )
57
- for instance in ec2 .instances .page_size (count = 100 ):
58
- if instance .instance_lifecycle != 'spot' :
59
- #instance_types["%s %s" % (region,instance.instance_type)] += 1
60
- total_instances += 1
61
- #and check that coutn against the configured limit from EC2
62
- ec2c = sess .client ('ec2' , region_name = region )
63
- attributes = ec2c .describe_account_attributes (AttributeNames = ['max-instances' ])
64
- all_limits .append ({'region' :region , 'service' :'EC2' , 'limit' :'Max On-Demand Instances' , 'max' :attributes ['AccountAttributes' ][0 ]['AttributeValues' ][0 ]['AttributeValue' ], 'used' :total_instances })
65
-
66
- #inspect DMS resources and limits
67
- dms = sess .client ('dms' , region_name = region )
68
- dms_limits = dms .describe_account_attributes ()
69
- for dms_limit in dms_limits ['AccountQuotas' ]:
70
- all_limits .append ({'region' :region , 'service' :'DMS' , 'limit' :dms_limit ['AccountQuotaName' ], 'max' :dms_limit ['Max' ], 'used' :dms_limit ['Used' ]})
71
-
72
- #start the check from trusted advisor. remember that the check needs to be refreshed, so that should have been run at least an hour before hand
73
- # this is only run once per account since TA is global.
74
- support = sess .client ('support' )
75
- ta_resp = support .describe_trusted_advisor_check_result (checkId = 'eW7HH0l7J9' , language = 'en' )
76
- #walk the list of TA resources, and add them to the master list if valid
77
- if 'flaggedResources' in ta_resp ['result' ]:
78
- for limit in ta_resp ['result' ]['flaggedResources' ]:
79
- limit_dict = ta_limit_to_dict (limit ['metadata' ])
80
- if limit_dict != None :
81
- all_limits .append (limit_dict )
82
- account_out ['limits' ] = all_limits
83
- report_out .append (account_out )
84
-
85
- headers = ['AccountID' ,'Region' ,'Service' ,'Limit' ,'Used' ,'Max' ,'% Usage' ]
86
- csvfile = open ('/opt/staging/limits/' + now + report_filename , 'w' )
87
- csvwriter = csv .writer (csvfile , quoting = csv .QUOTE_ALL )
88
-
89
- csvwriter .writerow (headers )
90
- for account in report_out :
91
- for limit in sorted (account ['limits' ], key = lambda x : str (x ['region' ]) + str (x ['service' ]) + str (x ['limit' ])):
92
- csvwriter .writerow ([account ['id' ],limit ['region' ], limit ['service' ], limit ['limit' ], limit ['used' ], limit ['max' ],
93
- str (int (float (limit ['used' ])/ float (limit ['max' ])* 100 )) + '%' ])
98
+ try :
99
+ sess = get_role_session .get_role_session (account_id , role_name , role_session_name )
100
+ #instance_types = defaultdict(int)
101
+ for region in regions :
102
+ #get total EC2 instances since TA does not check TOTAL on-demand instances, only by instance type
103
+ total_instances = 0
104
+ ec2 = sess .resource ('ec2' , region_name = region )
105
+ for instance in ec2 .instances .page_size (count = 100 ):
106
+ if instance .instance_lifecycle != 'spot' :
107
+ #instance_types["%s %s" % (region,instance.instance_type)] += 1
108
+ total_instances += 1
109
+ #and check that coutn against the configured limit from EC2
110
+ ec2c = sess .client ('ec2' , region_name = region )
111
+ attributes = ec2c .describe_account_attributes (AttributeNames = ['max-instances' ])
112
+ all_limits .append ({'region' :region , 'service' :'EC2' , 'limit' :'Max On-Demand Instances' , 'max' :attributes ['AccountAttributes' ][0 ]['AttributeValues' ][0 ]['AttributeValue' ], 'used' :total_instances })
113
+
114
+ #inspect DMS resources and limits
115
+ dms = sess .client ('dms' , region_name = region )
116
+ dms_limits = dms .describe_account_attributes ()
117
+ for dms_limit in dms_limits ['AccountQuotas' ]:
118
+ all_limits .append ({'region' :region , 'service' :'DMS' , 'limit' :dms_limit ['AccountQuotaName' ], 'max' :dms_limit ['Max' ], 'used' :dms_limit ['Used' ]})
119
+
120
+ #start the check from trusted advisor. remember that the check needs to be refreshed, so that should have been run at least an hour before hand
121
+ # this is only run once per account since TA is global.
122
+ support = sess .client ('support' )
123
+ ta_resp = support .describe_trusted_advisor_check_result (checkId = 'eW7HH0l7J9' , language = 'en' )
124
+ #walk the list of TA resources, and add them to the master list if valid
125
+ if 'flaggedResources' in ta_resp ['result' ]:
126
+ for limit in ta_resp ['result' ]['flaggedResources' ]:
127
+ limit_dict = ta_limit_to_dict (limit ['metadata' ])
128
+ if limit_dict != None :
129
+ all_limits .append (limit_dict )
130
+ account_out ['limits' ] = all_limits
131
+ report_out .append (account_out )
132
+
133
+ headers = ['AccountID' ,'Region' ,'Service' ,'Limit' ,'Used' ,'Max' ,'% Usage' ]
134
+ csvfilelocation = '/opt/staging/limits/' + now + report_filename
135
+ csvfile = open (csvfilelocation , 'w' )
136
+ csvwriter = csv .writer (csvfile , quoting = csv .QUOTE_ALL )
137
+
138
+ is_exceeding_limit = None
139
+
140
+ csvwriter .writerow (headers )
141
+ for account in report_out :
142
+ for limit in sorted (account ['limits' ], key = lambda x : str (x ['region' ]) + str (x ['service' ]) + str (x ['limit' ])):
143
+ limit_percent = int (float (limit ['used' ])/ float (limit ['max' ])* 100 )
144
+
145
+ try :
146
+ if reporting_limit is not None and limit_percent >= int (reporting_limit ):
147
+ is_exceeding_limit = True
148
+ except ValueError :
149
+ print ("Could not convert data to an integer." , sys .exc_info ()[0 ])
150
+
151
+
152
+ csvwriter .writerow ([account ['id' ],limit ['region' ], limit ['service' ], limit ['limit' ], limit ['used' ], limit ['max' ],
153
+ str (limit_percent ) + '%' ])
94
154
155
+ if is_exceeding_limit :
156
+ send_mail_attachment (message , csvfilelocation )
95
157
158
+ except Exception :
159
+ print ("Unexpected error:" , sys .exc_info ()[0 ])
160
+ csvfile = open ('/opt/staging/limits/' + now + report_filename , 'w' )
161
+ csvwriter = csv .writer (csvfile , quoting = csv .QUOTE_ALL )
162
+ csvwriter .writerow (["ERROR" , traceback .format_exc ()])
163
+ raise
0 commit comments