Skip to content

Commit 4d143ce

Browse files
committed
Add core function with pyLambda structure
1 parent 2e1f9c4 commit 4d143ce

File tree

2 files changed

+334
-0
lines changed

2 files changed

+334
-0
lines changed

pylambda/monitor_lambda/event.json

Whitespace-only changes.

pylambda/monitor_lambda/service.py

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import division
3+
import boto3
4+
import re
5+
import requests
6+
import json
7+
from botocore.exceptions import ClientError
8+
import config
9+
10+
11+
def dynamodb_scan(dynamo_client, table_name):
12+
table = dynamo_client.Table(table_name)
13+
try:
14+
response = table.scan()
15+
except ClientError as e:
16+
print(e.response['Error']['Message'])
17+
else:
18+
if 'Items' not in response:
19+
return None
20+
else:
21+
item = response['Items']
22+
return item
23+
return None
24+
25+
26+
def dynamodb_insert_data(dynamodb, table_name, content_arr):
27+
result = False
28+
table = dynamodb.Table(table_name)
29+
try:
30+
insert_result = table.put_item(
31+
Item=content_arr
32+
)
33+
if insert_result['ResponseMetadata']['HTTPStatusCode'] == 200:
34+
result = True
35+
except ClientError as e:
36+
print(e.response['Error']['Message'])
37+
38+
return result
39+
40+
41+
'''
42+
dynamodb = boto3.resource("dynamodb",
43+
aws_access_key_id=config.aws_access_key_id,
44+
aws_secret_access_key=config.aws_secret_access_key,
45+
region_name=config.aws_region)
46+
content_arr = {
47+
"monitor_id": "TEST2",
48+
"alert": False,
49+
"warning": False
50+
}
51+
52+
insert_result = dynamodb_insert_data(dynamodb, config.table_name_current, content_arr)
53+
print insert_result
54+
'''
55+
56+
57+
def dynamodb_read_pkey(dynamo_client, table_name, key, index):
58+
table = dynamo_client.Table(table_name)
59+
try:
60+
response = table.get_item(
61+
Key={
62+
key: str(index)
63+
}
64+
)
65+
except ClientError as e:
66+
print(e.response['Error']['Message'])
67+
else:
68+
if 'Item' not in response:
69+
return None
70+
else:
71+
item = response['Item']
72+
return item
73+
return None
74+
75+
76+
'''
77+
dynamo_client = boto3.resource("dynamodb",
78+
aws_access_key_id=config.aws_access_key_id,
79+
aws_secret_access_key=config.aws_secret_access_key,
80+
region_name=config.aws_region)
81+
82+
83+
item = dynamodb_read_pkey(dynamo_client, config.table_name_current, 'monitor_id', 'Cyclops-Beta-SignIn_FailedRate')
84+
print item
85+
'''
86+
87+
88+
def dynamodb_delete_item(dynamo_client, table_name, key, value):
89+
table = dynamo_client.Table(table_name)
90+
try:
91+
table.delete_item(
92+
Key={
93+
key: value
94+
}
95+
)
96+
except ClientError as e:
97+
return False
98+
else:
99+
return True
100+
101+
102+
'''
103+
dynamo_client = boto3.resource("dynamodb",
104+
aws_access_key_id=config.aws_access_key_id,
105+
aws_secret_access_key=config.aws_secret_access_key,
106+
region_name=config.aws_region)
107+
delete_result = dynamodb_delete_item(dynamo_client, config.table_name_current, 'monitor_id', 'TEST2')
108+
print delete_result
109+
'''
110+
111+
112+
def get_log_hit(elk_conn, json_data):
113+
r = requests.post(elk_conn + "?size=0", data=json_data)
114+
json_res = json.loads(r.text)
115+
return json_res['hits']['total']
116+
117+
118+
def get_log_dict_value(elk_conn, json_data, value_param):
119+
120+
r = requests.post(elk_conn + "?size=0", data=json_data)
121+
json_res = json.loads(r.text)
122+
123+
dict_list = value_param.split(".")
124+
125+
str_key = ""
126+
for key in dict_list:
127+
str_key += '[\'' + key + '\']'
128+
129+
return eval("json_res" + str_key)
130+
131+
132+
def slack_notify(slack_webhook, post_data):
133+
r = requests.post(slack_webhook, data=post_data)
134+
if r == '200':
135+
return True
136+
else:
137+
return False
138+
139+
#post_data = {"text": "Test"}
140+
#slack_notify("http://SLACK_WEB_HOOK_URL", json.dumps(post_data))
141+
142+
143+
def monitor_current_update(monitor_id, alarm_level, alarm_bool):
144+
dynamo_client = boto3.resource("dynamodb",
145+
aws_access_key_id=config.aws_access_key_id,
146+
aws_secret_access_key=config.aws_secret_access_key,
147+
region_name=config.aws_region)
148+
table = dynamo_client .Table(config.table_name_current)
149+
try:
150+
table.update_item(
151+
Key={
152+
'monitor_id': monitor_id
153+
},
154+
UpdateExpression="set " + alarm_level + " = :p",
155+
ExpressionAttributeValues={
156+
':p': alarm_bool
157+
},
158+
ReturnValues="UPDATED_NEW"
159+
)
160+
except ClientError:
161+
return False
162+
else:
163+
return True
164+
165+
166+
def check_monitor_current_status(monitor_id, alarm_level):
167+
dynamo_client = boto3.resource("dynamodb",
168+
aws_access_key_id=config.aws_access_key_id,
169+
aws_secret_access_key=config.aws_secret_access_key,
170+
region_name=config.aws_region)
171+
172+
item = dynamodb_read_pkey(dynamo_client, config.table_name_current, 'monitor_id', monitor_id)
173+
if item is not None:
174+
return item[alarm_level]
175+
else:
176+
return None
177+
178+
179+
#r = check_monitor_current_status('TEST2', 'warning')
180+
#print r
181+
182+
183+
def handle_monitor_current(monitor_id, alarm_level, alarm_bool):
184+
alarm_status = "NoChange" # NoChange; Recovered; Start
185+
dynamodb = boto3.resource("dynamodb",
186+
aws_access_key_id=config.aws_access_key_id,
187+
aws_secret_access_key=config.aws_secret_access_key,
188+
region_name=config.aws_region)
189+
190+
current_status = check_monitor_current_status(monitor_id, alarm_level)
191+
if current_status is True:
192+
if alarm_bool is False:
193+
monitor_current_update(monitor_id, alarm_level, alarm_bool)
194+
alarm_status = "Recovered"
195+
elif current_status is False:
196+
if alarm_bool is True:
197+
monitor_current_update(monitor_id, alarm_level, alarm_bool)
198+
alarm_status = "Start"
199+
elif current_status is None:
200+
if alarm_level == 'warning':
201+
content_arr = {
202+
"monitor_id": monitor_id,
203+
'warning': alarm_bool,
204+
'alert': False
205+
}
206+
elif alarm_level == 'alert':
207+
content_arr = {
208+
"monitor_id": monitor_id,
209+
'warning': False,
210+
'alert': alarm_bool
211+
}
212+
dynamodb_insert_data(dynamodb, config.table_name_current, content_arr)
213+
if alarm_bool is True:
214+
alarm_status = "Start"
215+
216+
return alarm_status
217+
218+
219+
# NoChange; Recovered; Start
220+
#alarm_status = handle_monitor_current("TEST3", "warning", True)
221+
#print alarm_status
222+
223+
224+
def handle_notify(monitor_id, cal_result, config_alarms, config_notify, alarm_status_warning, alarm_status_alert):
225+
send_notify = ""
226+
#print "[bool_warning]" + str(bool_warning) + "[alarm_status_warning]" + alarm_status_warning + "[bool_alert]" + str(bool_alert) + "[alarm_status_alert]" + alarm_status_alert
227+
# alarm_status: NoChange; Recovered; Start
228+
# Warning
229+
if alarm_status_warning != 'NoChange':
230+
send_notify = "warning"
231+
# Slack Notify
232+
if 'slack' in config_notify:
233+
slack_webhook = config_notify['slack']
234+
warning_data = {"text": "[" + alarm_status_warning + "]" + "[WARN] " + monitor_id + ": " + str(cal_result) + config_alarms['warning']}
235+
slack_notify(slack_webhook, json.dumps(warning_data))
236+
237+
# Alert
238+
if alarm_status_alert != 'NoChange':
239+
send_notify += "alert"
240+
# Slack Notify
241+
if 'slack' in config_notify:
242+
slack_webhook = config_notify['slack']
243+
alert_data = {"text": "[" + alarm_status_alert + "]" + "[ALERT] " + monitor_id + ": " + str(cal_result) + config_alarms['alert']}
244+
slack_notify(slack_webhook, json.dumps(alert_data))
245+
246+
return send_notify
247+
248+
249+
def handler(event, context):
250+
dynamo_client = boto3.resource("dynamodb",
251+
aws_access_key_id=config.aws_access_key_id,
252+
aws_secret_access_key=config.aws_secret_access_key,
253+
region_name=config.aws_region)
254+
items = dynamodb_scan(dynamo_client, config.table_name_config)
255+
return_list = []
256+
257+
for i in range(len(items)):
258+
enable = items[i]['enable']
259+
if enable:
260+
monitor_id = items[i]['monitor_id']
261+
262+
elk_env = items[i]['elk_env']
263+
if elk_env == 'staging':
264+
elk_conn = config.elk_stg
265+
elif elk_env == 'production':
266+
elk_conn = config.elk_prod
267+
268+
formula = items[i]['formula']
269+
270+
# Replace ${} params
271+
pattern = re.compile(r'\$\{([A-Za-z0-9_.]+)\}')
272+
params = re.findall(pattern, formula)
273+
dict_log_count = {}
274+
for param in params:
275+
param_log_count = get_log_hit(elk_conn, json.dumps(items[i]['parameters'][param]))
276+
dict_log_count.update({"${"+param+"}": param_log_count})
277+
278+
# Replace %{} params
279+
pattern_2 = re.compile(r'%\{([A-Za-z0-9_.]+)\}')
280+
params_2 = re.findall(pattern_2, formula)
281+
dict_log_value = {}
282+
for param in params_2:
283+
param_log_value = get_log_dict_value(elk_conn, json.dumps(items[i]['parameters'][param]), param)
284+
dict_log_value.update({"%{"+param+"}": param_log_value})
285+
286+
# Replace formula for calculation
287+
formula_replaced = formula
288+
289+
for key, value in dict_log_count.iteritems():
290+
formula_replaced = formula_replaced.replace(key, str(value))
291+
292+
for key, value in dict_log_value.iteritems():
293+
formula_replaced = formula_replaced.replace(key, str(value))
294+
295+
try:
296+
#cal_result = float(eval(formula_replaced))
297+
cal_result = eval(formula_replaced)
298+
299+
except ZeroDivisionError:
300+
raise Exception("Division by zero")
301+
302+
config_alarms = items[i]['alarms']
303+
if 'warning' in config_alarms:
304+
bool_warning = eval(str(cal_result) + config_alarms['warning'])
305+
alarm_status_warning = handle_monitor_current(monitor_id, "warning", bool_warning)
306+
307+
if 'alert' in config_alarms:
308+
bool_alert = eval(str(cal_result) + config_alarms['alert'])
309+
alarm_status_alert = handle_monitor_current(monitor_id, "alert", bool_alert)
310+
311+
config_notify = items[i]['notify']
312+
send_notify = handle_notify(monitor_id, cal_result, config_alarms, config_notify, alarm_status_warning, alarm_status_alert)
313+
314+
monitor_arr = {
315+
"monitor_id": monitor_id,
316+
"elk_env": elk_env,
317+
"elk_conn": elk_conn,
318+
"dict_log_count": dict_log_count,
319+
"formula": formula,
320+
"formula_replaced": formula_replaced,
321+
"cal_result": cal_result,
322+
"config_alarms": config_alarms,
323+
"bool_warning": str(bool_warning),
324+
"alarm_status_warning": alarm_status_warning,
325+
"bool_alert": str(bool_alert),
326+
"alarm_status_alert": alarm_status_alert,
327+
"send_notify": send_notify
328+
}
329+
return_list.append(monitor_arr)
330+
331+
return return_list
332+
333+
#result = handler(None, None)
334+
#print json.dumps(result)

0 commit comments

Comments
 (0)