5d330b0a8c3f23cd2bf2f135c0eb43802299a726
[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 logging
28
29 log = logging.getLogger(__name__)
30
31 try:
32 import boto
33 import boto.ec2
34 import boto.vpc
35 import boto.ec2.cloudwatch
36 import boto.ec2.connection
37 except:
38 exit("Boto not avialable. Try activating your virtualenv OR `pip install boto`")
39
40 STATISTICS = {
41 "AVERAGE": "Average",
42 "MINIMUM": "Minimum",
43 "MAXIMUM": "Maximum",
44 "COUNT": "SampleCount",
45 "SUM": "Sum"}
46
47 OPERATIONS = {
48 "GE": ">=",
49 "LE": "<=",
50 "GT": ">",
51 "LT": "<",
52 "EQ": "="}
53
54
55 class MetricAlarm():
56 """Alarms Functionality Handler -- Carries out alarming requests and responses via BOTO.Cloudwatch """
57
58 def __init__(self):
59 self.alarm_resp = dict()
60 self.del_resp = dict()
61
62 def config_alarm(self, cloudwatch_conn, create_info):
63 """Configure or Create a new alarm"""
64 inner_dict = dict()
65 """ Alarm Name to ID Mapping """
66 alarm_info = create_info['alarm_create_request']
67 alarm_id = alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid']
68 if self.is_present(cloudwatch_conn, alarm_id)['status'] == True:
69 alarm_id = None
70 log.debug("Alarm already exists, Try updating the alarm using 'update_alarm_configuration()'")
71 return alarm_id
72 else:
73 try:
74 if alarm_info['statistic'] in STATISTICS:
75 if alarm_info['operation'] in OPERATIONS:
76 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
77 connection=cloudwatch_conn,
78 name=alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid'],
79 metric=alarm_info['metric_name'],
80 namespace="AWS/EC2",
81 statistic=STATISTICS[alarm_info['statistic']],
82 comparison=OPERATIONS[alarm_info['operation']],
83 threshold=alarm_info['threshold_value'],
84 period=60,
85 evaluation_periods=1,
86 unit=alarm_info['unit'],
87 description=alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
88 dimensions={'InstanceId': alarm_info['resource_uuid']},
89 alarm_actions=None,
90 ok_actions=None,
91 insufficient_data_actions=None)
92
93 """Setting Alarm Actions :
94 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
95
96 status = cloudwatch_conn.put_metric_alarm(alarm)
97
98 log.debug("Alarm Configured Succesfully")
99 self.alarm_resp['schema_version'] = str(create_info['schema_version'])
100 self.alarm_resp['schema_type'] = 'create_alarm_response'
101
102 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
103 inner_dict['alarm_uuid'] = str(alarm_id)
104 inner_dict['status'] = status
105
106 self.alarm_resp['alarm_create_response'] = inner_dict
107
108 if status == True:
109 return self.alarm_resp
110 else:
111 return None
112 else:
113 log.error("Operation not supported")
114 return None
115 else:
116 log.error("Statistic not supported")
117 return None
118 except Exception as e:
119 log.error("Alarm Configuration Failed: " + str(e))
120
121 def update_alarm(self, cloudwatch_conn, update_info):
122
123 """Update or reconfigure an alarm"""
124 inner_dict = dict()
125 alarm_info = update_info['alarm_update_request']
126
127 """Alarm Name to ID Mapping"""
128 alarm_id = alarm_info['alarm_uuid']
129 status = self.is_present(cloudwatch_conn, alarm_id)
130
131 """Verifying : Alarm exists already"""
132 if status['status'] == False:
133 alarm_id = None
134 log.debug("Alarm not found, Try creating the alarm using 'configure_alarm()'")
135 return alarm_id
136 else:
137 try:
138 if alarm_info['statistic'] in STATISTICS:
139 if alarm_info['operation'] in OPERATIONS:
140 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
141 connection=cloudwatch_conn,
142 name=status['info'].name,
143 metric=alarm_info['metric_name'],
144 namespace="AWS/EC2",
145 statistic=STATISTICS[alarm_info['statistic']],
146 comparison=OPERATIONS[alarm_info['operation']],
147 threshold=alarm_info['threshold_value'],
148 period=60,
149 evaluation_periods=1,
150 unit=alarm_info['unit'],
151 description=alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
152 dimensions={'InstanceId': str(status['info'].dimensions['InstanceId']).split("'")[1]},
153 alarm_actions=None,
154 ok_actions=None,
155 insufficient_data_actions=None)
156
157 """Setting Alarm Actions :
158 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
159
160 status = cloudwatch_conn.put_metric_alarm(alarm)
161 log.debug("Alarm %s Updated ", alarm.name)
162 self.alarm_resp['schema_version'] = str(update_info['schema_version'])
163 self.alarm_resp['schema_type'] = 'update_alarm_response'
164
165 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
166 inner_dict['alarm_uuid'] = str(alarm_id)
167 inner_dict['status'] = status
168
169 self.alarm_resp['alarm_update_response'] = inner_dict
170 return self.alarm_resp
171 else:
172 log.error("Operation not supported")
173 return None
174 else:
175 log.error("Statistic not supported")
176 return None
177 except Exception as e:
178 log.error("Error in Updating Alarm " + str(e))
179
180 def delete_Alarm(self, cloudwatch_conn, del_info_all):
181
182 """Deletes an Alarm with specified alarm_id"""
183 inner_dict = dict()
184 del_info = del_info_all['alarm_delete_request']
185 status = self.is_present(cloudwatch_conn, del_info['alarm_uuid'])
186 try:
187 if status['status'] == True:
188 del_status = cloudwatch_conn.delete_alarms(status['info'].name)
189 self.del_resp['schema_version'] = str(del_info_all['schema_version'])
190 self.del_resp['schema_type'] = 'delete_alarm_response'
191 inner_dict['correlation_id'] = str(del_info['correlation_id'])
192 inner_dict['alarm_id'] = str(del_info['alarm_uuid'])
193 inner_dict['status'] = del_status
194 self.del_resp['alarm_deletion_response'] = inner_dict
195 return self.del_resp
196 return None
197 except Exception as e:
198 log.error("Alarm Not Deleted: " + str(e))
199
200 def alarms_list(self, cloudwatch_conn, list_info):
201
202 """Get a list of alarms that are present on a particular VIM type"""
203 alarm_list = []
204 alarm_info = dict()
205 inner_dict = list_info['alarm_list_request']
206 try: # id vim
207 alarms = cloudwatch_conn.describe_alarms()
208 itr = 0
209 for alarm in alarms:
210 list_info['alarm_list_request']['alarm_uuid'] = str(alarm.description).split(';')[1]
211
212 # Severity = alarm_name = resource_uuid = ""
213 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict[
214 'resource_uuid'] == "":
215 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
216 itr += 1
217 # alarm_name = resource_uuid = ""
218 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] == "" and \
219 inner_dict['resource_uuid'] == "":
220 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
221 itr += 1
222 # severity = resource_uuid = ""
223 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict[
224 'resource_uuid'] == "":
225 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
226 itr += 1
227 # severity = alarm_name = ""
228 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == \
229 str(alarm.dimensions['InstanceId']).split("'")[1]:
230 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
231 itr += 1
232 # resource_uuid = ""
233 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict[
234 'alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
235 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
236 itr += 1
237 # alarm_name = ""
238 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] == "" and \
239 inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
240 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
241 itr += 1
242 # severity = ""
243 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict[
244 '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[
249 'alarm_name'] in alarm.name and inner_dict['resource_uuid'] == \
250 str(alarm.dimensions['InstanceId']).split("'")[1]:
251 alarm_list.insert(itr, self.alarm_details(cloudwatch_conn, list_info))
252 itr += 1
253
254 alarm_info['schema_version'] = str(list_info['schema_version'])
255 alarm_info['schema_type'] = 'list_alarm_response'
256 alarm_info['list_alarm_response'] = alarm_list
257
258 return alarm_info
259 except Exception as e:
260 log.error("Error in Getting List : %s", str(e))
261
262 def alarm_details(self, cloudwatch_conn, ack_info):
263
264 """Get an individual alarm details specified by alarm_name"""
265 try:
266 alarms_details = cloudwatch_conn.describe_alarm_history()
267 alarm_details_all = dict()
268 alarm_details_dict = dict()
269 ack_info_all = ack_info
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))