-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Julia Aghamyan
committed
Oct 26, 2023
1 parent
acca4a6
commit c0e1603
Showing
14 changed files
with
635 additions
and
280 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
219 changes: 219 additions & 0 deletions
219
modules/cloudwatch-alarm-actions/modules/lambda-subscription/src/jira/event_handler.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
import logging | ||
import json | ||
import os | ||
import boto3 | ||
import base64 | ||
|
||
from urllib.request import Request, urlopen | ||
from urllib.error import URLError, HTTPError | ||
|
||
LOGLEVEL = os.environ.get('LOG_LEVEL', 'INFO').upper() | ||
FALLBACK_SUBJECT = 'AWS Alerts' | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel(getattr(logging, LOGLEVEL)) | ||
|
||
logger.info("log level: {}".format(LOGLEVEL)) | ||
|
||
def event_handler_for_metrics(metrics_body): | ||
|
||
for metrics_this in metrics_body: | ||
if 'MetricStat' in metrics_this: | ||
metric_stat = metrics_this["MetricStat"] | ||
|
||
metric = metric_stat["Metric"] | ||
metric_name = metric["MetricName"] | ||
metric_namespace = metric["Namespace"] | ||
|
||
dimension_string = "" | ||
MetricWidget = {} | ||
|
||
for dimension_object in metric["Dimensions"]: | ||
dimension_string += dimension_object["name"] + \ | ||
"/" + dimension_object["value"] + "/" | ||
|
||
if len(metric["Dimensions"]) == 1: | ||
MetricWidget = { | ||
"width": 600, | ||
"height": 395, | ||
"metrics": [ | ||
[ | ||
metric["Namespace"], | ||
metric_name, | ||
metric["Dimensions"][0]["name"], | ||
metric["Dimensions"][0]["value"], | ||
{ | ||
"stat": "Average" | ||
} | ||
] | ||
], | ||
"period": 300, | ||
"view": "timeSeries" | ||
} | ||
elif not len(metric["Dimensions"]): | ||
MetricWidget = { | ||
"width": 300, | ||
"height": 200, | ||
"metrics": [ | ||
[ | ||
metric["Namespace"], | ||
metric_name, | ||
metric["MetricName"], | ||
metric["Namespace"], | ||
{ | ||
"stat": "Average" | ||
} | ||
] | ||
], | ||
"period": 300, | ||
"view": "timeSeries" | ||
} | ||
else: | ||
MetricWidget = { | ||
"width": 600, | ||
"height": 395, | ||
"metrics": [ | ||
[ | ||
metric["Namespace"], | ||
metric_name, | ||
metric["Dimensions"][1]["name"], | ||
metric["Dimensions"][1]["value"], | ||
metric["Dimensions"][2]["name"], | ||
metric["Dimensions"][2]["value"], | ||
metric["Dimensions"][0]["name"], | ||
metric["Dimensions"][0]["value"], | ||
{ | ||
"stat": "Average" | ||
} | ||
] | ||
], | ||
"period": 300, | ||
"view": "timeSeries" | ||
} | ||
return MetricWidget,dimension_string,metric_namespace,metric_name | ||
|
||
def event_handler_for_expression(metrics_body): | ||
MetricWidget = {} | ||
metrics_widget = [] | ||
metric_namespace = [] | ||
metric_name = [] | ||
|
||
for metrics_this in metrics_body: | ||
temp_metric = {} | ||
if 'Expression' in metrics_this: | ||
expression = metrics_this["Expression"] | ||
if 'MetricStat' in metrics_this: | ||
metric_stat = metrics_this["MetricStat"] | ||
metric = metric_stat["Metric"] | ||
temp_metric["Namespace"] = metric["Namespace"] | ||
temp_metric["MetricName"] = metric["MetricName"] | ||
temp_metric["id"] = metrics_this["Id"] | ||
temp_metric["Name"] = str(metric["Dimensions"][0]["name"]) | ||
temp_metric["Value"] = str(metric["Dimensions"][0]["value"]) | ||
|
||
metric_name.append(temp_metric["MetricName"]) | ||
metric_namespace.append(temp_metric["Namespace"]) | ||
metrics_widget.append(temp_metric) | ||
metrics = [] | ||
for item in metrics_widget: | ||
temp = [] | ||
temp.append( item["Namespace"]) | ||
temp.append( item["MetricName"]) | ||
temp.append( item["Name"]) | ||
temp.append( item["Value"]) | ||
temp.append( {'id': item["id"]}) | ||
|
||
print(temp) | ||
metrics.append(temp) | ||
|
||
metrics.append([{"expression" : expression}]) | ||
|
||
|
||
MetricWidget = { | ||
"width": 600, | ||
"height": 395, | ||
"metrics": metrics, | ||
"period": 300, | ||
"view": "timeSeries" | ||
} | ||
return MetricWidget,expression,metric_namespace,metric_name | ||
|
||
def event_handler(event, context): | ||
"""send message via teams""" | ||
|
||
print("Event",event) | ||
print("Context",context) | ||
|
||
url = "https://" + os.environ['REGION'] + ".console.aws.amazon.com/cloudwatch/home?region=" + \ | ||
os.environ['REGION'] + "#alarmsV2:?~(alarmStateFilter~'ALARM)t" | ||
|
||
logger.debug("Event: {}".format(event)) | ||
message = str(event['Records'][0]['Sns']['Message']) | ||
subject = guess_subject(event) | ||
logger.debug("Event:" + str(event)) | ||
logger.debug("Message: " + str(subject)) | ||
logger.debug("Message: " + str(message)) | ||
|
||
# Json handle and cut info | ||
json_body = json.loads(event["Records"][0]["Sns"]["Message"]) | ||
# json_body = event["Records"][0]["Sns"]["Message"] #TODO: can be removed. This was used to debug lambda on fly | ||
trigger_body = json_body["Trigger"] | ||
aws_account = json_body["AWSAccountId"] | ||
aws_alarmdescription = json_body["AlarmDescription"] | ||
dimension_string = "" | ||
|
||
if "Metrics" in trigger_body: | ||
metrics_body = trigger_body["Metrics"] | ||
logger.debug("> Alarm Metrics Body Value", metrics_body) | ||
print("metrics_body: ",metrics_body) | ||
# processing one single metric | ||
# TODO: we need to manage multiple metrics processing | ||
# for metrics_this in metrics_body: | ||
# try: | ||
# metric_stat = metrics_this["MetricStat"] | ||
|
||
# except KeyError: | ||
# logger.debug("MetricStat is missing in this metric block") | ||
for metric_this in metrics_body: | ||
if metric_this["ReturnData"] == True: | ||
if "Expression" in metric_this: | ||
MetricWidget,dimension_string,metric_namespace,metric_name = event_handler_for_expression(metrics_body) | ||
alert_type = "Expression" | ||
else: | ||
alert_type = "Metric" | ||
MetricWidget,dimension_string,metric_namespace,metric_name = event_handler_for_metrics(metrics_body) | ||
|
||
else: | ||
for dimension_object in trigger_body["Dimensions"]: | ||
dimension_string += dimension_object["name"] + \ | ||
"/" + dimension_object["value"] + "/" | ||
|
||
metric_name = trigger_body["MetricName"] | ||
metric_namespace = trigger_body["Namespace"] | ||
MetricWidget = { | ||
"width": 600, | ||
"height": 395, | ||
"metrics": [ | ||
[ | ||
metric_namespace, | ||
metric_name, | ||
trigger_body["Dimensions"][0]["name"], | ||
trigger_body["Dimensions"][0]["value"], | ||
{ | ||
"stat": "Minimum" | ||
} | ||
] | ||
], | ||
"period": 60, | ||
"view": "timeSeries", | ||
} | ||
|
||
# Get Cloudwatch metric widget, encoded base64 | ||
cloudwatch = boto3.client('cloudwatch', region_name=os.environ['REGION']) | ||
response = cloudwatch.get_metric_widget_image( | ||
MetricWidget=json.dumps(MetricWidget)) | ||
encoded_data = base64.b64encode( | ||
response["MetricWidgetImage"]).decode('utf-8') | ||
image = 'data:image/png;base64, {}'.format(encoded_data) | ||
|
||
return alert_type,subject,aws_account,aws_alarmdescription,dimension_string,metric_namespace,metric_name,image,url |
68 changes: 68 additions & 0 deletions
68
modules/cloudwatch-alarm-actions/modules/lambda-subscription/src/jira/lambda.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import os | ||
import requests | ||
from event_handler import event_handler | ||
|
||
def create_jira_ticket(summary,description): | ||
# Jira API URL and authentication | ||
url = os.environ['JIRA_URL'] | ||
username = os.environ['JIRA_USERNAME'] | ||
password = os.environ['JIRA_PASSWORD'] | ||
issue_data = { | ||
"fields": { | ||
"project": {"key": os.environ['JIRA_KEY']}, | ||
"summary": summary, | ||
"description": description, | ||
"issuetype": {"name": "Task"}, | ||
"labels": ["DevOps"] | ||
} | ||
} | ||
|
||
response = requests.post(url, json=issue_data, auth=(username, password)) | ||
if response.status_code == 201: | ||
print("Issue created successfully. Issue key:", response.json()['key']) | ||
else: | ||
print("Failed to create issue. Status code:", response.status_code) | ||
print("Response content:", response.content) | ||
|
||
|
||
def handler(event, context): | ||
alert_type,subject,aws_account,aws_alarmdescription,dimension_string,metric_namespace,metric_name,image,url = event_handler(event,context) | ||
|
||
if alert_type == "Expression": | ||
all_data = [ | ||
{ | ||
"title": "AccountId", | ||
"value": aws_account | ||
}, { | ||
"title": "Expression", | ||
"value": dimension_string | ||
},{ | ||
"title": "Metrics", | ||
"value": str(metric_name) | ||
},{ | ||
"title": "Namespaces", | ||
"value": str(metric_namespace) | ||
} | ||
] | ||
else: | ||
all_data = [ | ||
{ | ||
"title": "AccountId", | ||
"value": aws_account | ||
}, { | ||
"title": "Dimensions", | ||
"value": dimension_string | ||
}, { | ||
"title": "Metric", | ||
"value": metric_namespace + "/" + metric_name | ||
} | ||
] | ||
|
||
ok_check = "OK" in subject | ||
if not ok_check: | ||
description = f"\n{aws_alarmdescription}" | ||
description += f"\n h2. Details\n" | ||
description += f"\n".join([f"{item['title']}: {item['value']}" for item in all_data]) | ||
description += f"\nURL: {url}" | ||
print("Create jira ticket") | ||
create_jira_ticket(subject,description) |
Oops, something went wrong.