Updated code with listing functions and error handling
[osm/MON.git] / 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 else:
78 try:
79 if alarm_info['statistic'] in STATISTICS:
80 if alarm_info['operation'] in OPERATIONS:
81 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
82 connection = cloudwatch_conn,
83 name = alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid'],
84 metric = alarm_info['metric_name'],
85 namespace = "AWS/EC2",
86 statistic = STATISTICS[alarm_info['statistic']],
87 comparison = OPERATIONS[alarm_info['operation']],
88 threshold = alarm_info['threshold_value'],
89 period = 60,
90 evaluation_periods = 1,
91 unit=alarm_info['unit'],
92 description = alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
93 dimensions = {'InstanceId':alarm_info['resource_uuid']},
94 alarm_actions = None,
95 ok_actions = None,
96 insufficient_data_actions = None)
97
98 """Setting Alarm Actions :
99 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
100
101 status=cloudwatch_conn.put_metric_alarm(alarm)
102
103 log.debug ("Alarm Configured Succesfully")
104 self.alarm_resp['schema_version'] = str(create_info['schema_version'])
105 self.alarm_resp['schema_type'] = 'create_alarm_response'
106
107 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
108 inner_dict['alarm_uuid'] = str(alarm_id)
109 inner_dict['status'] = status
110
111 self.alarm_resp['alarm_create_response'] = inner_dict
112
113 if status == True:
114 return self.alarm_resp
115 else:
116 return None
117 else:
118 log.error("Operation not supported")
119 return None
120 else:
121 log.error("Statistic not supported")
122 return None
123 except Exception as e:
124 log.error("Alarm Configuration Failed: " + str(e))
125
126 #-----------------------------------------------------------------------------------------------------------------------------
127 def update_alarm(self,cloudwatch_conn,update_info):
128
129 """Update or reconfigure an alarm"""
130 inner_dict = dict()
131 alarm_info = update_info['alarm_update_request']
132
133 """Alarm Name to ID Mapping"""
134 alarm_id = alarm_info['alarm_uuid']
135 status = self.is_present(cloudwatch_conn,alarm_id)
136
137 """Verifying : Alarm exists already"""
138 if status['status'] == False:
139 alarm_id = None
140 log.debug("Alarm not found, Try creating the alarm using 'configure_alarm()'")
141 return alarm_id
142 else:
143 try:
144 if alarm_info['statistic'] in STATISTICS:
145 if alarm_info['operation'] in OPERATIONS:
146 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
147 connection = cloudwatch_conn,
148 name = status['info'].name ,
149 metric = alarm_info['metric_name'],
150 namespace = "AWS/EC2",
151 statistic = STATISTICS[alarm_info['statistic']],
152 comparison = OPERATIONS[alarm_info['operation']],
153 threshold = alarm_info['threshold_value'],
154 period = 60,
155 evaluation_periods = 1,
156 unit=alarm_info['unit'],
157 description = alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
158 dimensions = {'InstanceId':str(status['info'].dimensions['InstanceId']).split("'")[1]},
159 alarm_actions = None,
160 ok_actions = None,
161 insufficient_data_actions = None)
162
163 """Setting Alarm Actions :
164 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
165
166 status=cloudwatch_conn.put_metric_alarm(alarm)
167 log.debug("Alarm %s Updated ",alarm.name)
168 self.alarm_resp['schema_version'] = str(update_info['schema_version'])
169 self.alarm_resp['schema_type'] = 'update_alarm_response'
170
171 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
172 inner_dict['alarm_uuid'] = str(alarm_id)
173 inner_dict['status'] = status
174
175 self.alarm_resp['alarm_update_response'] = inner_dict
176 return self.alarm_resp
177 else:
178 log.error("Operation not supported")
179 return None
180 else:
181 log.error("Statistic not supported")
182 return None
183 except Exception as e:
184 log.error ("Error in Updating Alarm " + str(e))
185
186 #-----------------------------------------------------------------------------------------------------------------------------
187 def delete_Alarm(self,cloudwatch_conn,del_info_all):
188
189 """Deletes an Alarm with specified alarm_id"""
190 inner_dict = dict()
191 del_info = del_info_all['alarm_delete_request']
192 status = self.is_present(cloudwatch_conn,del_info['alarm_uuid'])
193 try:
194 if status['status'] == True:
195 del_status=cloudwatch_conn.delete_alarms(status['info'].name)
196 self.del_resp['schema_version'] = str(del_info_all['schema_version'])
197 self.del_resp['schema_type'] = 'delete_alarm_response'
198 inner_dict['correlation_id'] = str(del_info['correlation_id'])
199 inner_dict['alarm_id'] = str(del_info['alarm_uuid'])
200 inner_dict['status'] = del_status
201 self.del_resp['alarm_deletion_response'] = inner_dict
202 return self.del_resp
203 return None
204 except Exception as e:
205 log.error("Alarm Not Deleted: " + str(e))
206 #-----------------------------------------------------------------------------------------------------------------------------
207 def alarms_list(self,cloudwatch_conn,list_info):
208
209 """Get a list of alarms that are present on a particular VIM type"""
210 alarm_list = []
211 alarm_info = dict()
212 inner_dict = list_info['alarm_list_request']
213 try: #id vim
214 alarms = cloudwatch_conn.describe_alarms()
215 itr = 0
216 for alarm in alarms:
217 list_info['alarm_list_request']['alarm_uuid'] = str(alarm.description).split(';')[1]
218
219 #Severity = alarm_name = resource_uuid = ""
220 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == "":
221 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
222 itr += 1
223 #alarm_name = resource_uuid = ""
224 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == "":
225 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
226 itr += 1
227 #severity = resource_uuid = ""
228 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
229 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
230 itr += 1
231 #severity = alarm_name = ""
232 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
233 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
234 itr += 1
235 #resource_uuid = ""
236 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
237 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
238 itr += 1
239 #alarm_name = ""
240 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]:
241 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
242 itr += 1
243 #severity = ""
244 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
245 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
246 itr += 1
247 #Everything provided
248 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]:
249 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
250 itr += 1
251
252 alarm_info['schema_version'] = str(list_info['schema_version'])
253 alarm_info['schema_type'] = 'list_alarm_response'
254 alarm_info['list_alarm_resp'] = alarm_list
255
256 return alarm_info
257 except Exception as e:
258 log.error("Error in Getting List : %s",str(e))
259 #-----------------------------------------------------------------------------------------------------------------------------
260 def alarm_details(self,cloudwatch_conn,ack_info):
261
262 """Get an individual alarm details specified by alarm_name"""
263 try:
264 alarms_details=cloudwatch_conn.describe_alarm_history()
265 alarm_details_all = dict()
266 alarm_details_dict = dict()
267 ack_info_all = ack_info
268
269
270 if 'ack_details' in ack_info:
271 ack_info = ack_info['ack_details']
272 elif 'alarm_list_request' in ack_info:
273 ack_info = ack_info['alarm_list_request']
274
275 is_present = self.is_present(cloudwatch_conn,ack_info['alarm_uuid'])
276
277 for itr in range (len(alarms_details)):
278 if alarms_details[itr].name == is_present['info'].name :#name, timestamp, summary
279 if 'created' in alarms_details[itr].summary:
280 alarm_details_dict['status'] = "New"
281 elif 'updated' in alarms_details[itr].summary:
282 alarm_details_dict['status'] = "Update"
283 elif 'deleted' in alarms_details[itr].summary:
284 alarm_details_dict['status'] = "Canceled"
285
286 status = alarms_details[itr].summary.split()
287 alarms = cloudwatch_conn.describe_alarms()
288 for alarm in alarms:
289 if str(alarm.description).split(';')[1] == ack_info['alarm_uuid']:
290 alarm_details_dict['alarm_uuid'] = str(ack_info['alarm_uuid'])
291 alarm_details_dict['resource_uuid'] = str(alarm.dimensions['InstanceId']).split("'")[1]
292 alarm_details_dict['description'] = str(alarm.description).split(';')[1]
293 alarm_details_dict['severity'] = str(alarm.description).split(';')[0]
294 alarm_details_dict['start_date_time'] = str(alarms_details[itr].timestamp)
295 alarm_details_dict['vim_type'] = str(ack_info_all['vim_type'])
296 #TODO : tenant id
297 if 'ack_details' in ack_info_all:
298 alarm_details_all['schema_version'] = str(ack_info_all['schema_version'])
299 alarm_details_all['schema_type'] = 'notify_alarm'
300 alarm_details_all['notify_details'] = alarm_details_dict
301 return alarm_details_all
302
303 elif 'alarm_list_request' in ack_info_all:
304 return alarm_details_dict
305
306 except Exception as e:
307 log.error("Error getting alarm details: %s",str(e))
308 #-----------------------------------------------------------------------------------------------------------------------------
309 def is_present(self,cloudwatch_conn,alarm_id):
310 """Finding alarm from already configured alarms"""
311 alarm_info = dict()
312 try:
313 alarms = cloudwatch_conn.describe_alarms()
314 for alarm in alarms:
315 if str(alarm.description).split(';')[1] == alarm_id:
316 alarm_info['status'] = True
317 alarm_info['info'] = alarm
318 return alarm_info
319 alarm_info['status'] = False
320 return alarm_info
321 except Exception as e:
322 log.error("Error Finding Alarm",str(e))
323 #-----------------------------------------------------------------------------------------------------------------------------
324