8d38e2f3e407217bbef651995c205e1338888856
[osm/MON.git] / osm_mon / plugins / CloudWatch / metric_alarms.py
1 ##
2 # Copyright 2017 xFlow Research Pvt. Ltd
3 # This file is part of MON module
4 # All Rights Reserved.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); you may
7 # not use this file except in compliance with the License. You may obtain
8 # a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15 # License for the specific language governing permissions and limitations
16 # under the License.
17 #
18 # For those usages not covered by the Apache License, Version 2.0 please
19 # contact with: wajeeha.hamid@xflowresearch.com
20 ##
21
22 ''' Handling of alarms requests via BOTO 2.48 '''
23
24 __author__ = "Wajeeha Hamid"
25 __date__ = "18-September-2017"
26
27 import sys
28 import os
29 import re
30 import datetime
31 import random
32 import json
33 import logging as log
34 from random import randint
35 from operator import itemgetter
36 from connection import Connection
37
38
39 try:
40 import boto
41 import boto.ec2
42 import boto.vpc
43 import boto.ec2.cloudwatch
44 import boto.ec2.connection
45 except:
46 exit("Boto not avialable. Try activating your virtualenv OR `pip install boto`")
47
48 STATISTICS = {
49 "AVERAGE": "Average",
50 "MINIMUM": "Minimum",
51 "MAXIMUM": "Maximum",
52 "COUNT" : "SampleCount",
53 "SUM" : "Sum"}
54
55 OPERATIONS = {
56 "GE" : ">=",
57 "LE" : "<=",
58 "GT" : ">",
59 "LT" : "<",
60 "EQ" : "="}
61
62 class MetricAlarm():
63 """Alarms Functionality Handler -- Carries out alarming requests and responses via BOTO.Cloudwatch """
64 def __init__(self):
65 self.alarm_resp = dict()
66 self.del_resp = dict()
67
68 def config_alarm(self,cloudwatch_conn,create_info):
69 """Configure or Create a new alarm"""
70 inner_dict = dict()
71 """ Alarm Name to ID Mapping """
72 alarm_info = create_info['alarm_create_request']
73 alarm_id = alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid']
74 if self.is_present(cloudwatch_conn,alarm_id)['status'] == True:
75 alarm_id = None
76 log.debug ("Alarm already exists, Try updating the alarm using 'update_alarm_configuration()'")
77 return alarm_id
78 else:
79 try:
80 if alarm_info['statistic'] in STATISTICS:
81 if alarm_info['operation'] in OPERATIONS:
82 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
83 connection = cloudwatch_conn,
84 name = alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid'],
85 metric = alarm_info['metric_name'],
86 namespace = "AWS/EC2",
87 statistic = STATISTICS[alarm_info['statistic']],
88 comparison = OPERATIONS[alarm_info['operation']],
89 threshold = alarm_info['threshold_value'],
90 period = 60,
91 evaluation_periods = 1,
92 unit=alarm_info['unit'],
93 description = alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
94 dimensions = {'InstanceId':alarm_info['resource_uuid']},
95 alarm_actions = None,
96 ok_actions = None,
97 insufficient_data_actions = None)
98
99 """Setting Alarm Actions :
100 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
101
102 status=cloudwatch_conn.put_metric_alarm(alarm)
103
104 log.debug ("Alarm Configured Succesfully")
105 self.alarm_resp['schema_version'] = str(create_info['schema_version'])
106 self.alarm_resp['schema_type'] = 'create_alarm_response'
107
108 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
109 inner_dict['alarm_uuid'] = str(alarm_id)
110 inner_dict['status'] = status
111
112 self.alarm_resp['alarm_create_response'] = inner_dict
113
114 if status == True:
115 return self.alarm_resp
116 else:
117 return None
118 else:
119 log.error("Operation not supported")
120 return None
121 else:
122 log.error("Statistic not supported")
123 return None
124 except Exception as e:
125 log.error("Alarm Configuration Failed: " + str(e))
126
127 #-----------------------------------------------------------------------------------------------------------------------------
128 def update_alarm(self,cloudwatch_conn,update_info):
129
130 """Update or reconfigure an alarm"""
131 inner_dict = dict()
132 alarm_info = update_info['alarm_update_request']
133
134 """Alarm Name to ID Mapping"""
135 alarm_id = alarm_info['alarm_uuid']
136 status = self.is_present(cloudwatch_conn,alarm_id)
137
138 """Verifying : Alarm exists already"""
139 if status['status'] == False:
140 alarm_id = None
141 log.debug("Alarm not found, Try creating the alarm using 'configure_alarm()'")
142 return alarm_id
143 else:
144 try:
145 if alarm_info['statistic'] in STATISTICS:
146 if alarm_info['operation'] in OPERATIONS:
147 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
148 connection = cloudwatch_conn,
149 name = status['info'].name ,
150 metric = alarm_info['metric_name'],
151 namespace = "AWS/EC2",
152 statistic = STATISTICS[alarm_info['statistic']],
153 comparison = OPERATIONS[alarm_info['operation']],
154 threshold = alarm_info['threshold_value'],
155 period = 60,
156 evaluation_periods = 1,
157 unit=alarm_info['unit'],
158 description = alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
159 dimensions = {'InstanceId':str(status['info'].dimensions['InstanceId']).split("'")[1]},
160 alarm_actions = None,
161 ok_actions = None,
162 insufficient_data_actions = None)
163
164 """Setting Alarm Actions :
165 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
166
167 status=cloudwatch_conn.put_metric_alarm(alarm)
168 log.debug("Alarm %s Updated ",alarm.name)
169 self.alarm_resp['schema_version'] = str(update_info['schema_version'])
170 self.alarm_resp['schema_type'] = 'update_alarm_response'
171
172 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
173 inner_dict['alarm_uuid'] = str(alarm_id)
174 inner_dict['status'] = status
175
176 self.alarm_resp['alarm_update_response'] = inner_dict
177 return self.alarm_resp
178 else:
179 log.error("Operation not supported")
180 return None
181 else:
182 log.error("Statistic not supported")
183 return None
184 except Exception as e:
185 log.error ("Error in Updating Alarm " + str(e))
186
187 #-----------------------------------------------------------------------------------------------------------------------------
188 def delete_Alarm(self,cloudwatch_conn,del_info_all):
189
190 """Deletes an Alarm with specified alarm_id"""
191 inner_dict = dict()
192 del_info = del_info_all['alarm_delete_request']
193 status = self.is_present(cloudwatch_conn,del_info['alarm_uuid'])
194 try:
195 if status['status'] == True:
196 del_status=cloudwatch_conn.delete_alarms(status['info'].name)
197 self.del_resp['schema_version'] = str(del_info_all['schema_version'])
198 self.del_resp['schema_type'] = 'delete_alarm_response'
199 inner_dict['correlation_id'] = str(del_info['correlation_id'])
200 inner_dict['alarm_id'] = str(del_info['alarm_uuid'])
201 inner_dict['status'] = del_status
202 self.del_resp['alarm_deletion_response'] = inner_dict
203 return self.del_resp
204 return None
205 except Exception as e:
206 log.error("Alarm Not Deleted: " + str(e))
207 #-----------------------------------------------------------------------------------------------------------------------------
208 def alarms_list(self,cloudwatch_conn,list_info):
209
210 """Get a list of alarms that are present on a particular VIM type"""
211 alarm_list = []
212 alarm_info = dict()
213 inner_dict = list_info['alarm_list_request']
214 try: #id vim
215 alarms = cloudwatch_conn.describe_alarms()
216 itr = 0
217 for alarm in alarms:
218 list_info['alarm_list_request']['alarm_uuid'] = str(alarm.description).split(';')[1]
219
220 #Severity = alarm_name = resource_uuid = ""
221 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == "":
222 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
223 itr += 1
224 #alarm_name = resource_uuid = ""
225 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == "":
226 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
227 itr += 1
228 #severity = resource_uuid = ""
229 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
230 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
231 itr += 1
232 #severity = alarm_name = ""
233 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
234 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
235 itr += 1
236 #resource_uuid = ""
237 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
238 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
239 itr += 1
240 #alarm_name = ""
241 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
242 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
243 itr += 1
244 #severity = ""
245 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
246 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
247 itr += 1
248 #Everything provided
249 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
250 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
251 itr += 1
252
253 alarm_info['schema_version'] = str(list_info['schema_version'])
254 alarm_info['schema_type'] = 'list_alarm_response'
255 alarm_info['list_alarm_resp'] = alarm_list
256
257 return alarm_info
258 except Exception as e:
259 log.error("Error in Getting List : %s",str(e))
260 #-----------------------------------------------------------------------------------------------------------------------------
261 def alarm_details(self,cloudwatch_conn,ack_info):
262
263 """Get an individual alarm details specified by alarm_name"""
264 try:
265 alarms_details=cloudwatch_conn.describe_alarm_history()
266 alarm_details_all = dict()
267 alarm_details_dict = dict()
268 ack_info_all = ack_info
269
270
271 if 'ack_details' in ack_info:
272 ack_info = ack_info['ack_details']
273 elif 'alarm_list_request' in ack_info:
274 ack_info = ack_info['alarm_list_request']
275
276 is_present = self.is_present(cloudwatch_conn,ack_info['alarm_uuid'])
277
278 for itr in range (len(alarms_details)):
279 if alarms_details[itr].name == is_present['info'].name :#name, timestamp, summary
280 if 'created' in alarms_details[itr].summary:
281 alarm_details_dict['status'] = "New"
282 elif 'updated' in alarms_details[itr].summary:
283 alarm_details_dict['status'] = "Update"
284 elif 'deleted' in alarms_details[itr].summary:
285 alarm_details_dict['status'] = "Canceled"
286
287 status = alarms_details[itr].summary.split()
288 alarms = cloudwatch_conn.describe_alarms()
289 for alarm in alarms:
290 if str(alarm.description).split(';')[1] == ack_info['alarm_uuid']:
291 alarm_details_dict['alarm_uuid'] = str(ack_info['alarm_uuid'])
292 alarm_details_dict['resource_uuid'] = str(alarm.dimensions['InstanceId']).split("'")[1]
293 alarm_details_dict['description'] = str(alarm.description).split(';')[1]
294 alarm_details_dict['severity'] = str(alarm.description).split(';')[0]
295 alarm_details_dict['start_date_time'] = str(alarms_details[itr].timestamp)
296 alarm_details_dict['vim_type'] = str(ack_info_all['vim_type'])
297 #TODO : tenant id
298 if 'ack_details' in ack_info_all:
299 alarm_details_all['schema_version'] = str(ack_info_all['schema_version'])
300 alarm_details_all['schema_type'] = 'notify_alarm'
301 alarm_details_all['notify_details'] = alarm_details_dict
302 return alarm_details_all
303
304 elif 'alarm_list_request' in ack_info_all:
305 return alarm_details_dict
306
307 except Exception as e:
308 log.error("Error getting alarm details: %s",str(e))
309 #-----------------------------------------------------------------------------------------------------------------------------
310 def is_present(self,cloudwatch_conn,alarm_id):
311 """Finding alarm from already configured alarms"""
312 alarm_info = dict()
313 try:
314 alarms = cloudwatch_conn.describe_alarms()
315 for alarm in alarms:
316 if str(alarm.description).split(';')[1] == alarm_id:
317 alarm_info['status'] = True
318 alarm_info['info'] = alarm
319 return alarm_info
320 alarm_info['status'] = False
321 return alarm_info
322 except Exception as e:
323 log.error("Error Finding Alarm",str(e))
324 #-----------------------------------------------------------------------------------------------------------------------------
325