-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathautocomputeoptimizer.py
173 lines (157 loc) · 6.8 KB
/
autocomputeoptimizer.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
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
# igarcia 2024-05
# Version 2.3.1
# Automation for Compute Optimizer Recommendations
# It will change the EC2 Instance Type to a Recommendation of the AWS Compute Optimizer Service and send an email about it
# It won't do anything to AutoScaling Group's Instances
# You can set a TAG Value for the instances that this Lambda can manage
# IMPORTANT: your EC2 instance should endure a restart!
# UPDATE
# Now you can override the behaviour per instance with TAGBUSQUEDA's values of ACOO-OVER, ACOO-UNDER, ACOO-BOTH
# Now You can specify exceptions of family, size, or type
import os
import json
import boto3
RISK = os.environ['RISK'] #de 0 a 5, 0 es sin riesgo y 5 es mucho riesgo (No Risk, Very Low, Low, Medium, High, Very High)
TYPE = os.environ['TYPE'] # Overprovisioned, Underprovisioned or Both
TAGBUSQUEDA = os.environ['TAGBUSQUEDA']
TAGVALOR = os.environ['TAGVALOR']
TOPIC = os.environ['TOPIC']
CORREO = os.environ['CORREO']
MENSAJE = ""
OVERRIDES = ("ACOO-OVER","ACOO-UNDER","ACOO-BOTH")
EXCEPTIONS = os.getenv('EXCEPTIONS', default='nano').split(',')
EXCEPTIONS = [ exception.strip().lower() for exception in EXCEPTIONS] # To trimm and lowercase
risk_text = {"0":"No Risk", "1":"Very Low Risk", "2":"Low Risk", "3":"Medium Risk", "4":"High Risk", "5":"Very High Risk"}
ec2 = boto3.resource('ec2')
co_client = boto3.client('compute-optimizer')
sns = boto3.resource('sns')
def review_compute_optimizer_recos(instance):
global MENSAJE
cambio = 0
response = ""
ec2_id = instance['instanceArn'].split('/')[1] #Instance ID
ec2_name = instance['instanceName']
to_do = False # Flag to determine if Instance will be examined
tag_value = "" # To check for overrides
ec2_instance = ec2.Instance(ec2_id)
ec2_prev_type = ec2_instance.instance_type
ec2_tags = ec2_instance.tags
for tag in ec2_tags:
if tag['Key'] == TAGBUSQUEDA:
if tag['Value'] == TAGVALOR:
tag_value = TYPE # The type on Recommendation to apply
elif tag['Value'] in OVERRIDES:
tag_value = tag['Value'] # The override Recommendation to apply
# Revision sobre los OVERRIDES
# TYPE = ['Underprovisioned','Overprovisioned', 'Both']
# OVERRIDES = ACOO-OVER, ACOO-UNDER, ACOO-BOTH
# instance['finding'] = 'OVER_PROVISIONED'
if tag_value[0:4].upper() == instance['finding'][0:4].upper() or tag_value[0:4].upper() == "BOTH":
to_do = True
else:
to_do = False
if tag_value in OVERRIDES:
if tag_value[5:9].upper() == instance['finding'][0:4].upper() or tag_value[5:9].upper() == "BOTH":
to_do = True
else:
to_do = False
if to_do:
for option in instance['recommendationOptions']:
ec2_prev_type = ec2_instance.instance_type
ec2_new_type = option['instanceType']
if ec2_new_type in EXCEPTIONS:
continue
else:
for part in ec2_new_type.split('.'):
if part in EXCEPTIONS:
ec2_prev_type = ec2_new_type # Para prevenir el cambio de tipo de Instancia
if (int(option['performanceRisk']) <= int(RISK)) and (ec2_prev_type != ec2_new_type): # El riesgo debe ser aceptable y el tipo de Instancia debe cambiar
#Hacer Cambio Tipo de Instancia
if ec2_instance.state['Name'] == 'stopped':
try:
response = ec2_instance.modify_attribute(InstanceType={'Value':ec2_new_type})
response = ec2_instance.start()
response = ec2_instance.wait_until_running()
cambio = 1
MENSAJE = MENSAJE + "Info: Instance " + ec2_name + " changed to " + ec2_new_type + "\n"
print("Se modificó Instancia {} - {} de {} a tipo {} ".format(ec2_id, ec2_name, ec2_prev_type, ec2_new_type))
break
except:
ec2_instance.stop()
ec2_instance.wait_until_stopped()
ec2_instance.modify_attribute(InstanceType={'Value':ec2_prev_type})
ec2_instance.start()
cambio = 0
MENSAJE = MENSAJE + "Error: Instance " + ec2_name + " NOT changed to " + ec2_new_type + "\n"
print(response)
print("No se puedo modificar Instancia {} - {} a tipo {} ".format(ec2_id, ec2_name, ec2_new_type))
break
elif ec2_instance.state['Name'] == 'running':
try:
response = ec2_instance.stop()
response = ec2_instance.wait_until_stopped()
response = ec2_instance.modify_attribute(InstanceType={'Value':ec2_new_type})
response = ec2_instance.start()
response = ec2_instance.wait_until_running()
cambio = 1
MENSAJE = MENSAJE + "Info: Instance " + ec2_name + " changed to " + ec2_new_type + "\n"
print("Se modificó Instancia {} - {} de {} a tipo {} ".format(ec2_id, ec2_name, ec2_prev_type, ec2_new_type))
break
except:
ec2_instance.stop()
ec2_instance.wait_until_stopped()
ec2_instance.modify_attribute(InstanceType={'Value':ec2_prev_type})
ec2_instance.start()
cambio = 0
MENSAJE = MENSAJE + "Error: Instance " + ec2_name + " NOT changed to " + ec2_new_type + "\n"
print(response)
print("No se puedo modificar Instancia {} - {} a tipo {} ".format(ec2_id, ec2_name, ec2_new_type))
break
else:
print("Opción {} recomendada no viable para instancia {} - {} ".format(ec2_new_type, ec2_id, ec2_name))
if response == "":
MENSAJE = MENSAJE + "Info: Instance " + ec2_name + " with no viable options. \n"
else:
MENSAJE = MENSAJE + "Info: Nothing to do for Instance " + ec2_name + "\n"
print("No se modificó Instancia {} - {}.".format(ec2_id, ec2_name))
return cambio
def lambda_handler(event, context):
total = 0
cambios = 0
global MENSAJE
#if TYPE == "Both":
# R_TYPE = ['Underprovisioned','Overprovisioned']
#else:
# R_TYPE = [TYPE]
R_TYPE = ['Underprovisioned','Overprovisioned']
MENSAJE = ""
co_recos = co_client.get_ec2_instance_recommendations(filters=[{'name':'Finding','values':R_TYPE}])
for instance in co_recos['instanceRecommendations']:
total+=1
cambios = cambios + review_compute_optimizer_recos(instance)
while 'nextToken' in co_recos: #Pagineo
co_recos = co_client.get_ec2_instance_recommendations(
nextToken=co_recos['nextToken'],
filters=[{'name':'Finding','values':R_TYPE}]
)
for instance in co_recos['instanceRecommendations']:
total+=1
cambios = cambios + review_compute_optimizer_recos(instance)
the_message = "EC2 Instances updated "+str(cambios)+" of "+str(total)+" total recommendations:\n"
print("Se realizaron {} cambios con éxito de un total de {} sugeridos.".format(cambios,total))
print("Limite indicador de Riesgo: {}".format(risk_text[RISK]))
try:
if CORREO != "not@notify.me":
the_topic = sns.Topic(TOPIC)
the_message = the_message + MENSAJE
the_message = the_message + "\nDefined risk threshold to change instance type: " + risk_text[RISK] + "." + "\nMore information on Lambda log."
response = the_topic.publish(Subject="AutoComputeOptimizer Notification", Message=the_message)
except:
print(response)
print("Fallo al enviar mensaje por SNS.")
return {
'statusCode': 200,
'body': json.dumps(
'Lambda finalizada exitosamente.'
)
}