Merge "Path resolution for core producer"
[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
34 from random import randint
35 from operator import itemgetter
36 from connection import Connection
37
38 log = logging.getLogger(__name__)
39
40 try:
41 import boto
42 import boto.ec2
43 import boto.vpc
44 import boto.ec2.cloudwatch
45 import boto.ec2.connection
46 except:
47 exit("Boto not avialable. Try activating your virtualenv OR `pip install boto`")
48
49 STATISTICS = {
50 "AVERAGE": "Average",
51 "MINIMUM": "Minimum",
52 "MAXIMUM": "Maximum",
53 "COUNT" : "SampleCount",
54 "SUM" : "Sum"}
55
56 OPERATIONS = {
57 "GE" : ">=",
58 "LE" : "<=",
59 "GT" : ">",
60 "LT" : "<",
61 "EQ" : "="}
62
63 class MetricAlarm():
64 """Alarms Functionality Handler -- Carries out alarming requests and responses via BOTO.Cloudwatch """
65 def __init__(self):
66 self.alarm_resp = dict()
67 self.del_resp = dict()
68
69 def config_alarm(self,cloudwatch_conn,create_info):
70 """Configure or Create a new alarm"""
71 inner_dict = dict()
72 """ Alarm Name to ID Mapping """
73 alarm_info = create_info['alarm_create_request']
74 alarm_id = alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid']
75 if self.is_present(cloudwatch_conn,alarm_id)['status'] == True:
76 alarm_id = None
77 log.debug ("Alarm already exists, Try updating the alarm using 'update_alarm_configuration()'")
78 return alarm_id
79 else:
80 try:
81 if alarm_info['statistic'] in STATISTICS:
82 if alarm_info['operation'] in OPERATIONS:
83 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
84 connection = cloudwatch_conn,
85 name = alarm_info['alarm_name'] + "_" + alarm_info['resource_uuid'],
86 metric = alarm_info['metric_name'],
87 namespace = "AWS/EC2",
88 statistic = STATISTICS[alarm_info['statistic']],
89 comparison = OPERATIONS[alarm_info['operation']],
90 threshold = alarm_info['threshold_value'],
91 period = 60,
92 evaluation_periods = 1,
93 unit=alarm_info['unit'],
94 description = alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
95 dimensions = {'InstanceId':alarm_info['resource_uuid']},
96 alarm_actions = None,
97 ok_actions = None,
98 insufficient_data_actions = None)
99
100 """Setting Alarm Actions :
101 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
102
103 status=cloudwatch_conn.put_metric_alarm(alarm)
104
105 log.debug ("Alarm Configured Succesfully")
106 self.alarm_resp['schema_version'] = str(create_info['schema_version'])
107 self.alarm_resp['schema_type'] = 'create_alarm_response'
108
109 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
110 inner_dict['alarm_uuid'] = str(alarm_id)
111 inner_dict['status'] = status
112
113 self.alarm_resp['alarm_create_response'] = inner_dict
114
115 if status == True:
116 return self.alarm_resp
117 else:
118 return None
119 else:
120 log.error("Operation not supported")
121 return None
122 else:
123 log.error("Statistic not supported")
124 return None
125 except Exception as e:
126 log.error("Alarm Configuration Failed: " + str(e))
127
128 #-----------------------------------------------------------------------------------------------------------------------------
129 def update_alarm(self,cloudwatch_conn,update_info):
130
131 """Update or reconfigure an alarm"""
132 inner_dict = dict()
133 alarm_info = update_info['alarm_update_request']
134
135 """Alarm Name to ID Mapping"""
136 alarm_id = alarm_info['alarm_uuid']
137 status = self.is_present(cloudwatch_conn,alarm_id)
138
139 """Verifying : Alarm exists already"""
140 if status['status'] == False:
141 alarm_id = None
142 log.debug("Alarm not found, Try creating the alarm using 'configure_alarm()'")
143 return alarm_id
144 else:
145 try:
146 if alarm_info['statistic'] in STATISTICS:
147 if alarm_info['operation'] in OPERATIONS:
148 alarm = boto.ec2.cloudwatch.alarm.MetricAlarm(
149 connection = cloudwatch_conn,
150 name = status['info'].name ,
151 metric = alarm_info['metric_name'],
152 namespace = "AWS/EC2",
153 statistic = STATISTICS[alarm_info['statistic']],
154 comparison = OPERATIONS[alarm_info['operation']],
155 threshold = alarm_info['threshold_value'],
156 period = 60,
157 evaluation_periods = 1,
158 unit=alarm_info['unit'],
159 description = alarm_info['severity'] + ";" + alarm_id + ";" + alarm_info['description'],
160 dimensions = {'InstanceId':str(status['info'].dimensions['InstanceId']).split("'")[1]},
161 alarm_actions = None,
162 ok_actions = None,
163 insufficient_data_actions = None)
164
165 """Setting Alarm Actions :
166 alarm_actions = ['arn:aws:swf:us-west-2:465479087178:action/actions/AWS_EC2.InstanceId.Stop/1.0']"""
167
168 status=cloudwatch_conn.put_metric_alarm(alarm)
169 log.debug("Alarm %s Updated ",alarm.name)
170 self.alarm_resp['schema_version'] = str(update_info['schema_version'])
171 self.alarm_resp['schema_type'] = 'update_alarm_response'
172
173 inner_dict['correlation_id'] = str(alarm_info['correlation_id'])
174 inner_dict['alarm_uuid'] = str(alarm_id)
175 inner_dict['status'] = status
176
177 self.alarm_resp['alarm_update_response'] = inner_dict
178 return self.alarm_resp
179 else:
180 log.error("Operation not supported")
181 return None
182 else:
183 log.error("Statistic not supported")
184 return None
185 except Exception as e:
186 log.error ("Error in Updating Alarm " + str(e))
187
188 #-----------------------------------------------------------------------------------------------------------------------------
189 def delete_Alarm(self,cloudwatch_conn,del_info_all):
190
191 """Deletes an Alarm with specified alarm_id"""
192 inner_dict = dict()
193 del_info = del_info_all['alarm_delete_request']
194 status = self.is_present(cloudwatch_conn,del_info['alarm_uuid'])
195 try:
196 if status['status'] == True:
197 del_status=cloudwatch_conn.delete_alarms(status['info'].name)
198 self.del_resp['schema_version'] = str(del_info_all['schema_version'])
199 self.del_resp['schema_type'] = 'delete_alarm_response'
200 inner_dict['correlation_id'] = str(del_info['correlation_id'])
201 inner_dict['alarm_id'] = str(del_info['alarm_uuid'])
202 inner_dict['status'] = del_status
203 self.del_resp['alarm_deletion_response'] = inner_dict
204 return self.del_resp
205 return None
206 except Exception as e:
207 log.error("Alarm Not Deleted: " + str(e))
208 #-----------------------------------------------------------------------------------------------------------------------------
209 def alarms_list(self,cloudwatch_conn,list_info):
210
211 """Get a list of alarms that are present on a particular VIM type"""
212 alarm_list = []
213 alarm_info = dict()
214 inner_dict = list_info['alarm_list_request']
215 try: #id vim
216 alarms = cloudwatch_conn.describe_alarms()
217 itr = 0
218 for alarm in alarms:
219 list_info['alarm_list_request']['alarm_uuid'] = str(alarm.description).split(';')[1]
220
221 #Severity = alarm_name = resource_uuid = ""
222 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == "":
223 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
224 itr += 1
225 #alarm_name = resource_uuid = ""
226 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == "":
227 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
228 itr += 1
229 #severity = resource_uuid = ""
230 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
231 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
232 itr += 1
233 #severity = alarm_name = ""
234 if inner_dict['severity'] == "" and inner_dict['alarm_name'] == "" and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
235 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
236 itr += 1
237 #resource_uuid = ""
238 if inner_dict['severity'] == str(alarm.description).split(';')[0] and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == "":
239 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
240 itr += 1
241 #alarm_name = ""
242 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]:
243 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
244 itr += 1
245 #severity = ""
246 if inner_dict['severity'] == "" and inner_dict['alarm_name'] in alarm.name and inner_dict['resource_uuid'] == str(alarm.dimensions['InstanceId']).split("'")[1]:
247 alarm_list.insert(itr,self.alarm_details(cloudwatch_conn,list_info))
248 itr += 1
249 #Everything provided
250 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]:
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_resp'] = 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
272 if 'ack_details' in ack_info:
273 ack_info = ack_info['ack_details']
274 elif 'alarm_list_request' in ack_info:
275 ack_info = ack_info['alarm_list_request']
276
277 is_present = self.is_present(cloudwatch_conn,ack_info['alarm_uuid'])
278
279 for itr in range (len(alarms_details)):
280 if alarms_details[itr].name == is_present['info'].name :#name, timestamp, summary
281 if 'created' in alarms_details[itr].summary:
282 alarm_details_dict['status'] = "New"
283 elif 'updated' in alarms_details[itr].summary:
284 alarm_details_dict['status'] = "Update"
285 elif 'deleted' in alarms_details[itr].summary:
286 alarm_details_dict['status'] = "Canceled"
287
288 status = alarms_details[itr].summary.split()
289 alarms = cloudwatch_conn.describe_alarms()
290 for alarm in alarms:
291 if str(alarm.description).split(';')[1] == ack_info['alarm_uuid']:
292 alarm_details_dict['alarm_uuid'] = str(ack_info['alarm_uuid'])
293 alarm_details_dict['resource_uuid'] = str(alarm.dimensions['InstanceId']).split("'")[1]
294 alarm_details_dict['description'] = str(alarm.description).split(';')[1]
295 alarm_details_dict['severity'] = str(alarm.description).split(';')[0]
296 alarm_details_dict['start_date_time'] = str(alarms_details[itr].timestamp)
297 alarm_details_dict['vim_type'] = str(ack_info_all['vim_type'])
298 #TODO : tenant id
299 if 'ack_details' in ack_info_all:
300 alarm_details_all['schema_version'] = str(ack_info_all['schema_version'])
301 alarm_details_all['schema_type'] = 'notify_alarm'
302 alarm_details_all['notify_details'] = alarm_details_dict
303 return alarm_details_all
304
305 elif 'alarm_list_request' in ack_info_all:
306 return alarm_details_dict
307
308 except Exception as e:
309 log.error("Error getting alarm details: %s",str(e))
310 #-----------------------------------------------------------------------------------------------------------------------------
311 def is_present(self,cloudwatch_conn,alarm_id):
312 """Finding alarm from already configured alarms"""
313 alarm_info = dict()
314 try:
315 alarms = cloudwatch_conn.describe_alarms()
316 for alarm in alarms:
317 if str(alarm.description).split(';')[1] == alarm_id:
318 alarm_info['status'] = True
319 alarm_info['info'] = alarm
320 return alarm_info
321 alarm_info['status'] = False
322 return alarm_info
323 except Exception as e:
324 log.error("Error Finding Alarm",str(e))
325 #-----------------------------------------------------------------------------------------------------------------------------
326