cold
This commit is contained in:
7
AoM_Service/AoM_Configs/webapp/__init__.py
Executable file
7
AoM_Service/AoM_Configs/webapp/__init__.py
Executable file
@@ -0,0 +1,7 @@
|
||||
from flask import Flask, render_template, request, session
|
||||
app = Flask(__name__)
|
||||
app.config['SESSION_TYPE'] = 'filesystem'
|
||||
app.config['SECRET_KEY'] = 'super secret key'
|
||||
|
||||
import webapp.views
|
||||
|
||||
139
AoM_Service/AoM_Configs/webapp/render.py
Executable file
139
AoM_Service/AoM_Configs/webapp/render.py
Executable file
@@ -0,0 +1,139 @@
|
||||
import yaml
|
||||
import os
|
||||
import json
|
||||
import traceback
|
||||
import sys
|
||||
from library.logger import AlertLogging
|
||||
|
||||
logger = AlertLogging('aom')
|
||||
logger.start()
|
||||
|
||||
|
||||
def render_config(config):
|
||||
"""
|
||||
Reads in the config dict and renders to file. config usually from web interface
|
||||
Args:
|
||||
config: The config to use to generate the yaml file
|
||||
Returns:
|
||||
boolean string of 0 if successful and the yaml as string, or 1 and the error
|
||||
"""
|
||||
try:
|
||||
# GET THE NAME OF THE FILE FROM THE CONFIG
|
||||
file_name = ''.join([config['alert_name'], '.yaml'])
|
||||
logger.debug("Filename: {}".format(file_name))
|
||||
# THIS SHOULD BE A PARAMETER PASSED IN
|
||||
file_path = os.path.join('alert_configs', file_name)
|
||||
logger.debug("Full path: {}".format(file_path))
|
||||
# SANITIZE THE CONFIG TO A NEW OBJECT
|
||||
yaml_config = {'alerts': {},
|
||||
'id': config['alert_name'],
|
||||
'interval': 30 if int(config['interval']) < 30 else int(config['interval'])}
|
||||
# SET THE INTERVAL TO lowest value of 30 seconds
|
||||
# SPLIT THE ALERTS INTO A LIST
|
||||
if 'vo' in config:
|
||||
yaml_config['alerts']['vo'] = [x for x in config['vo_list'].split(',') if x]
|
||||
if 'email' in config:
|
||||
yaml_config['alerts']['email'] = [x for x in config['email_list'].split(',') if x]
|
||||
if 'slack' in config:
|
||||
yaml_config['alerts']['slack'] = [x for x in config['slack_list'].split(',') if x]
|
||||
# GET THRESHOLDS AS FLOATS
|
||||
if 'critical_threshold' in config:
|
||||
if config['critical_upper_threshold'] is not "":
|
||||
yaml_config['critical_upper_threshold'] = float(config['critical_threshold'])
|
||||
if 'critical_upper_threshold' in config:
|
||||
if config['critical_upper_threshold'] is not "":
|
||||
yaml_config['critical_upper_threshold'] = float(config['critical_upper_threshold'])
|
||||
if 'warning_threshold' in config:
|
||||
yaml_config['warning_upper_threshold'] = float(config['warning_threshold'])
|
||||
if 'warning_upper_threshold' in config:
|
||||
yaml_config['warning_upper_threshold'] = float(config['warning_upper_threshold'])
|
||||
if 'critical_lower_threshold' in config:
|
||||
if config['critical_lower_threshold'] is not "":
|
||||
yaml_config['critical_lower_threshold'] = float(config['critical_lower_threshold'])
|
||||
if 'warning_lower_threshold' in config:
|
||||
yaml_config['warning_lower_threshold'] = float(config['warning_lower_threshold'])
|
||||
if 'occurrences' in config:
|
||||
yaml_config['occurrences_threshold'] = int(config['occurrences_threshold'])
|
||||
# PARSE THE QUERY OUT INTO A DICT OBJECT
|
||||
if config['prometheus_query']:
|
||||
yaml_config['query_type'] = 'prometheus'
|
||||
yaml_config['prometheus_url'] = config['prometheus_url']
|
||||
yaml_config['query'] = config['prometheus_query']
|
||||
yaml_config['start_time'] = config['start_time']
|
||||
yaml_config['end_time'] = config['end_time']
|
||||
else:
|
||||
yaml_config['query_type'] = 'kairosdb'
|
||||
yaml_config['query'] = json.loads(config['kairosdb_query'])
|
||||
# GET THE TAGS, COMMA SEPARATED
|
||||
tags = config['tags'].split(',')
|
||||
yaml_config['tags'] = [x for x in tags if x]
|
||||
# GET THE URL
|
||||
yaml_config['url'] = config['url']
|
||||
# WRITE TO FILE
|
||||
yaml_str = yaml.dump(yaml_config, default_flow_style=False, explicit_start=True)
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(yaml_str)
|
||||
return 0, yaml_str
|
||||
except json.decoder.JSONDecodeError:
|
||||
return 1, "Query string is not valid json: {}".format(traceback.format_stack())
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Unable to render yaml config file to disk")
|
||||
_, _, ex_traceback = sys.exc_info()
|
||||
return 1, render_traceback(e, ex_traceback)
|
||||
|
||||
|
||||
def render_yaml(alert_id):
|
||||
"""
|
||||
Reads in a yaml file into the config that the web expects.
|
||||
Args:
|
||||
alert_id: then name of the config
|
||||
Returns:
|
||||
Dictionary
|
||||
"""
|
||||
file_name = ''.join([alert_id, '.yaml'])
|
||||
file_path = os.path.join('alert_configs', file_name)
|
||||
config = yaml.load(open(file_path, 'r').read())
|
||||
yaml_config = dict()
|
||||
yaml_config['alert_name'] = config['id']
|
||||
yaml_config['interval'] = config['interval']
|
||||
if 'critical_threshold' in config:
|
||||
yaml_config['critical_upper_threshold'] = config['critical_threshold']
|
||||
if 'critical_upper_threshold' in config:
|
||||
yaml_config['critical_upper_threshold'] = config['critical_upper_threshold']
|
||||
if 'critical_lower_threshold' in config:
|
||||
yaml_config['critical_lower_threshold'] = config['critical_lower_threshold']
|
||||
if 'warning_threshold' in config:
|
||||
yaml_config['warning_upper_threshold'] = config['warning_threshold']
|
||||
if 'warning_upper_threshold' in config:
|
||||
yaml_config['warning_upper_threshold'] = config['warning_upper_threshold']
|
||||
if 'warning_lower_threshold' in config:
|
||||
yaml_config['warning_lower_threshold'] = config['warning_lower_threshold']
|
||||
if 'occurrences_threshold' in config:
|
||||
yaml_config['occurrences_threshold'] = config['occurrences_threshold']
|
||||
yaml_config['url'] = config['url']
|
||||
if 'email' in config['alerts']:
|
||||
yaml_config['email'] = 'on'
|
||||
yaml_config['email_list'] = ','.join(config['alerts']['email'])
|
||||
if 'vo' in config['alerts']:
|
||||
yaml_config['vo'] = 'on'
|
||||
yaml_config['vo_list'] = ','.join(config['alerts']['vo'])
|
||||
if 'slack' in config['alerts']:
|
||||
yaml_config['slack'] = 'on'
|
||||
yaml_config['slack_list'] = ','.join(config['alerts']['slack'])
|
||||
if 'tags' in config:
|
||||
yaml_config['tags'] = ','.join(config['tags'])
|
||||
if config.get('query_type') == 'prometheus':
|
||||
yaml_config['prometheus_query'] = config['query']
|
||||
yaml_config['prometheus_url'] = config['prometheus_url']
|
||||
yaml_config['start_time'] = config['start_time']
|
||||
yaml_config['end_time'] = config['end_time']
|
||||
else:
|
||||
yaml_config['kairosdb_query'] = json.dumps(config['query'], sort_keys=True, indent=4, separators=(',', ': '))
|
||||
return yaml_config
|
||||
|
||||
|
||||
def render_traceback(ex, ex_traceback):
|
||||
tb_lines = traceback.format_exception(ex.__class__, ex, ex_traceback)
|
||||
logger.exception("Exception")
|
||||
return '\n'.join(tb_lines)
|
||||
14
AoM_Service/AoM_Configs/webapp/static/bootstrap-theme.min.css
vendored
Executable file
14
AoM_Service/AoM_Configs/webapp/static/bootstrap-theme.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
14
AoM_Service/AoM_Configs/webapp/static/bootstrap.min.css
vendored
Executable file
14
AoM_Service/AoM_Configs/webapp/static/bootstrap.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
11
AoM_Service/AoM_Configs/webapp/static/bootstrap.min.js
vendored
Executable file
11
AoM_Service/AoM_Configs/webapp/static/bootstrap.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
29
AoM_Service/AoM_Configs/webapp/static/style.css
Executable file
29
AoM_Service/AoM_Configs/webapp/static/style.css
Executable file
@@ -0,0 +1,29 @@
|
||||
body { font-family: sans-serif; background: #eee; }
|
||||
a, h1, h2 { color: #377BA8; }
|
||||
h1, h2 { font-family: 'Georgia', serif; margin: 0; }
|
||||
h1 { border-bottom: 2px solid #eee; }
|
||||
h2 { font-size: 1.2em; }
|
||||
|
||||
.page { margin: 2em auto; width: 45em; border: 5px solid #ccc;
|
||||
padding: 0.8em; background: white; }
|
||||
.entries { list-style: none; margin: 0; padding: 0; }
|
||||
.entries li { margin: 0.8em 1.2em; }
|
||||
.entries li h2 { margin-left: -1em; }
|
||||
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
|
||||
.add-entry dl { font-weight: bold; }
|
||||
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
|
||||
margin-bottom: 1em; background: #fafafa; }
|
||||
.flash { background: #CEE5F5; padding: 0.5em;
|
||||
border: 1px solid #AACBE2; }
|
||||
.error { background: #F0D6D6; padding: 0.5em; }
|
||||
/#.button { border-top: 2px solid #a3ceda;
|
||||
border-left: 2px solid #a3ceda;
|
||||
border-right: 2px solid #4f6267;
|
||||
border-bottom: 2px solid #4F6267;
|
||||
padding: 1px 20px !important;
|
||||
font-size: 14px !important;
|
||||
background-color: #CEE5F5;
|
||||
font-weight: bold;
|
||||
color: #2d525d; }
|
||||
#/
|
||||
.container { width: 500px; clear: both;}
|
||||
28
AoM_Service/AoM_Configs/webapp/templates/debug.html
Executable file
28
AoM_Service/AoM_Configs/webapp/templates/debug.html
Executable file
@@ -0,0 +1,28 @@
|
||||
{% extends "header.html" %}
|
||||
{% block body %}
|
||||
<h2>Form Elements</h2><br />
|
||||
<table>
|
||||
{% for key, value in query.items() %}
|
||||
<tr>
|
||||
<th> {{ key }} </th>
|
||||
<td> {{ value }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table><br/>
|
||||
<p>
|
||||
{{ query.alert_name }}
|
||||
</p>
|
||||
<h2>Rendered Config File</h2><br />
|
||||
<p>{{ file_path }}</p>
|
||||
<p>
|
||||
{% for line in file_contents %}
|
||||
<div>{{ line|safe }}</div>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<br />
|
||||
<form action="{{ url_for('re_build', alert_id=query.alert_name) }}" id="re_build" method="post">
|
||||
<p>
|
||||
<input type="submit" id="submit" class="btn btn-primary" value="Return to Form?">
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
6
AoM_Service/AoM_Configs/webapp/templates/error.html
Executable file
6
AoM_Service/AoM_Configs/webapp/templates/error.html
Executable file
@@ -0,0 +1,6 @@
|
||||
{% extends "header.html" %}
|
||||
{% block body %}
|
||||
<h1>Error Rendering config:</h1>
|
||||
<p>{{ message }}</p>
|
||||
<p><a href="{{ url_for('index') }}">Return to Creation Page?</a></p>
|
||||
{% endblock %}
|
||||
67
AoM_Service/AoM_Configs/webapp/templates/header.html
Executable file
67
AoM_Service/AoM_Configs/webapp/templates/header.html
Executable file
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, intial-scale=1">
|
||||
<title>Alerting On Metrics Yaml Builder</title>
|
||||
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='bootstrap.min.css') }}">
|
||||
<link rel="stylesheet" type=text/css href="{{ url_for('static', filename='style.css') }}">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='bootstrap.min.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
function dynInput(cbox) {
|
||||
console.log(cbox)
|
||||
if (cbox.checked) {
|
||||
var input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = cbox.name + "_list";
|
||||
input.name = cbox.name + "_list";
|
||||
document.getElementById("insertinputs_" + cbox.name).appendChild(input);
|
||||
} else {
|
||||
document.getElementById(cbox.name + "_list").remove();
|
||||
}
|
||||
}
|
||||
|
||||
function dynEnable(cbox) {
|
||||
console.log(cbox);
|
||||
var theId = "#" + cbox.name + "_list";
|
||||
console.log(theId);
|
||||
if (cbox.checked){
|
||||
$(theId)[0].disabled = false;
|
||||
} else {
|
||||
$(theId)[0].disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
function dynThreshold(cbox) {
|
||||
var theId = "#" + cbox.name + "_threshold";
|
||||
if (cbox.checked){
|
||||
$(theId)[0].disabled = false;
|
||||
} else {
|
||||
$(theId)[0].disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function forceLower(strInput){
|
||||
strInput.value=strInput.value.toLowerCase().replace(" ","_");
|
||||
}
|
||||
|
||||
function forceComma(strInput){
|
||||
strInput.value=strInput.value.replace(" ",",");
|
||||
}
|
||||
|
||||
function forcePositive(strInput){
|
||||
if (parseInt(strInput.value) <= 1) {
|
||||
strInput.value = 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class=page>
|
||||
{% block body %}{% endblock %}
|
||||
966
AoM_Service/AoM_Configs/webapp/templates/index.html
Executable file
966
AoM_Service/AoM_Configs/webapp/templates/index.html
Executable file
@@ -0,0 +1,966 @@
|
||||
{% extends "header.html" %}
|
||||
{% block body %}
|
||||
<form action="{{url_for('index')}}" id="builder" method="post" class="form-horizontal">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3 class="text-center">Alert Meta</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alert Name -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label for="alert_name" class="control-label">Alert Name:</label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#alertidModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="alertidModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myModalLabel">Alert Name</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The alert name acts as both the name of the .yaml file and the id for the alert. The
|
||||
alert name becomes part of what shows up in the title / subject when an alert is
|
||||
triggered</p>
|
||||
<p>Picking an alert name that already exists will overwrite the .yaml configuration file so
|
||||
be aware of what you choose</p>
|
||||
<p>The Alert name is also how this alert will show up in Victorops, Slack and Email
|
||||
(Depending on what options you choose for the Alerting</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input type="text" id="alert_name" class="form-control" name="alert_name" value="{{ alert_name }}"
|
||||
onkeyup="return forceLower(this);">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Check Interval -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="interval">Check Interval: </label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#intervalModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="intervalModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="checkInterval">Check Interval</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The check interval is how often the check will run the query (in seconds) and measure the
|
||||
results</p>
|
||||
<p>Anything less than 30 seconds will automatically be bumped up
|
||||
to 30 seconds. This is due to the fact that metrics are collected every 30 seconds, so
|
||||
checking more often than this would just result in the same values returned from the
|
||||
query
|
||||
as nothing would have changed yet</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<input type="number" id="interval" class="form-control" name="interval" value="{{ interval }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Upper Critical Threshold -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="criticalUpperThreshold">Upper Critical Threshold: </label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#criticalUpperThresholdModal">
|
||||
info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="criticalUpperThresholdModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="criticalUpperThresholdTitle">Critical Threshold</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This is a Floating Point or Int that when the results back from the query exceeds this
|
||||
number, a critical alert will trigger.</p>
|
||||
<p>Only Critical Alerts will also trigger emails and slack alerts (if set)</p>
|
||||
<p>Your query needs to be simplified down to just one or two
|
||||
values per grouping (A start and end metric). The alerting system will look at all
|
||||
values per grouping and check if any of the values are over the threshold to send out an
|
||||
alert</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<input type="number" class="form-control" id="criticalUpperThreshold" name="critical_upper_threshold"
|
||||
value="{{ critical_upper_threshold }}"
|
||||
step="0.01"
|
||||
onkeypress="validate(event)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lower Critical Threshold -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="criticalLowerThreshold">Lower Critical Threshold: </label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#criticalLowerThresholdModal">
|
||||
info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="criticalLowerThresholdModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="criticalLowerThresholdTitle">Lower Critical Threshold</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This is a Floating Point or Int that when the results back from the query drops below this
|
||||
number, a critical alert will trigger.</p>
|
||||
<p>Only Critical Alerts will also trigger emails and slack alerts (if set)</p>
|
||||
<p>Your query needs to be simplified down to just one or two
|
||||
values per grouping (A start and end metric). The alerting system will look at all
|
||||
values per grouping and check if any of the values are over the threshold to send out an
|
||||
alert</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<input type="number" class="form-control" id="lower_criticalThreshold" name="critical_lower_threshold"
|
||||
value="{{ critical_lower_threshold }}"
|
||||
step="0.01"
|
||||
onkeypress="validate(event)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Upper Warning Threshold -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="warningUpperThreshold">Upper Warning Threshold: </label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#warningUpperThresholdModal">
|
||||
info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="warningUpperThresholdModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="warningUpperThresholdTitle">Upper Warning Threshold</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This is a Floating Point or Int that when the results back from the query exceeds this
|
||||
number, a warning alert will trigger.</p>
|
||||
<p>Warnings will not trigger Email or Slack alerts (if set)</p>
|
||||
<p>Your query needs to be simplified down to just one or two
|
||||
values per grouping (A start and end metric). The alerting system will look at all
|
||||
values per grouping and check if any of the values are over the threshold to send out an
|
||||
alert</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
{% if warning_upper_threshold %}
|
||||
{% set warning_upper_checked='checked' %}
|
||||
{% else %}
|
||||
{% set warning_upper_disabled='disabled' %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="warning_upper" id="warning_upper" aria-label="..." onclick="dynThreshold(this);" {{
|
||||
warning_upper_checked }}>
|
||||
</span>
|
||||
<input type="number" name="warning_upper_threshold" class="form-control" id="warning_upper_threshold"
|
||||
value="{{ warning_upper_threshold }}"
|
||||
aria-label="..." step="0.01" {{ warning_upper_disabled }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lower Warning Threshold -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="warningLowerThreshold">Lower Warning Threshold: </label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#warningLowerThresholdModal">
|
||||
info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="warningLowerThresholdModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="warningLowerThresholdTitle">Lower Warning Threshold</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This is a Floating Point or Int that when the results back from the query drops below this
|
||||
number, a warning alert will trigger.</p>
|
||||
<p>Warnings will not trigger Email or Slack alerts (if set)</p>
|
||||
<p>Your query needs to be simplified down to just one or two
|
||||
values per grouping (A start and end metric). The alerting system will look at all
|
||||
values per grouping and check if any of the values are over the threshold to send out an
|
||||
alert</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
{% if warning_lower_threshold %}
|
||||
{% set warning_lower_checked='checked' %}
|
||||
{% else %}
|
||||
{% set warning_lower_disabled='disabled' %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="warning_lower" id="warning_lower" aria-label="..." onclick="dynThreshold(this);" {{
|
||||
warning_lower_checked }}>
|
||||
</span>
|
||||
<input type="number" name="warning_lower_threshold" class="form-control" id="warning_lower_threshold"
|
||||
value="{{ warning_lower_threshold }}"
|
||||
aria-label="..." step="0.01" {{ warning_lower_disabled }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Occurrences -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="occurrences_threshold">Frequency: </label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#occurrencesModal">
|
||||
info
|
||||
</button>
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="occurrencesModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="occurrencesTitle">Frequency</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>The occurrences value, when set, will determine how many times the alert has to exceed the
|
||||
threshold in order for an alert to trigger.</p>
|
||||
<p>This is particularly useful for metrics that can be spikey and resolve quickly,
|
||||
using occurrences allows you to only be alerted when a spike is no longer spiking but
|
||||
maintaining the rate over the period of time</p>
|
||||
<p>This is compared once every interval, so if your alert is set to 5 minutes, with a
|
||||
occurrences of 3, you'd have to have the threshold exceeded for 15 minutes before any
|
||||
alerts
|
||||
are sent out.</p>
|
||||
<p>The occurrences value is optional, and if not enabled, the service assumes that after 1 query
|
||||
exceeding the threshold is enough to trigger alerts. So in this way having an occurrences value
|
||||
set
|
||||
to 1 or not enabled does the same thing.</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
{% if occurrences_threshold and occurrences_threshold is number and occurrences_threshold > 1 %}
|
||||
{% set occurrences_checked='checked' %}
|
||||
{% else %}
|
||||
{% set occurrences_disabled='disabled' %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="occurrences" id="occurrences" aria-label="..."
|
||||
onclick="dynThreshold(this);" {{
|
||||
occurrences_checked }}>
|
||||
</span>
|
||||
<input type="number" name="occurrences_threshold" class="form-control" id="occurrences_threshold"
|
||||
value="{{ occurrences_threshold }}"
|
||||
aria-label="..." step="1" min="2" {{ occurrences_disabled }} onkeyup="return forcePositive(this);">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="tags">Tags:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#tagsModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="tagsModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="tagsTitle">Tags</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>A comma seperated list of tags used to include in the alert subject</p>
|
||||
<p>In the event of an alert, the tags will be used to look up distinctive
|
||||
information and
|
||||
include as part of the alert</p>
|
||||
<p>For example including the dc tag in an alert means that if an alert occurs, the
|
||||
alerting
|
||||
system will look up the dc value from the returned query and included it as part
|
||||
of the
|
||||
alert subject</p>
|
||||
<p>These are the same tag values used to build kiarosdb queries</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<input type="text" name="tags" id="tags" class="form-control" value="{{ tags }}" ,
|
||||
onkeyup="return forceComma(this);">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3 class="text-center">Notifications</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- VictorOps Alerts -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="vo">VictorOps Alert:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#voModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="voModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="voTitle">Victor Ops Alert List</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>A comma seperated list of victorops routing keys</p>
|
||||
<p>In the event of an alert, the Ids listed here will recieve a victorops alert</p>
|
||||
<p>If the checkbox isn't selected, when generating the .yaml config the values
|
||||
listed will
|
||||
be ignored</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
{% if vo=="on" %}
|
||||
{% set vo_checked='checked' %}
|
||||
{% else %}
|
||||
{% set vo_disabled='disabled' %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="vo" id="vo" aria-label="..." onclick="dynEnable(this);" {{ vo_checked
|
||||
}}>
|
||||
</span>
|
||||
<input type="text" class="form-control" name="vo_list" id="vo_list" aria-label="..."
|
||||
value="{{ vo_list }}" onkeyup="return forceComma(this);" {{ vo_disabled }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email Alerts -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="email">Email Alert:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#emailModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="emailModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="emailTitle">Email Alert List</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>A comma seperated list of email names to send alerts to</p>
|
||||
<p>In the event of an alert, the names listed here will recieve an email alert</p>
|
||||
<p>The alerting system appends an @qualtrics.com to the names listed here, so there
|
||||
is no
|
||||
need to include the @domain as it's assumed all alerting emails would go to a
|
||||
qualtrics
|
||||
address</p>
|
||||
<p>Also the SMTP server can only send to @qualtrics addresses anyways</p>
|
||||
<p>For example sending an email to both netops and devops on an alert would be <b>devops,netops</b>
|
||||
in the text box.</p>
|
||||
<p>If the checkbox isn't selected, when generating the .yaml config the values
|
||||
listed will
|
||||
be ignored</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
{% if email=="on" %}
|
||||
{% set email_checked='checked' %}
|
||||
{% else %}
|
||||
{% set email_disabled='disabled' %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="email" id="email" aria-label="..." onclick="dynEnable(this);" {{
|
||||
email_checked }}>
|
||||
</span>
|
||||
<input type="text" name="email_list" class="form-control" id="email_list"
|
||||
value="{{ email_list }}"
|
||||
aria-label="..." onkeyup="return forceComma(this);" {{ email_disabled }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Slack Alert List -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="slack">Slack Alert:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#slackModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="slackModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="slackTitle">Slack Alert List</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>A comma seperated list of slack names to send alerts to</p>
|
||||
<p>In the event of an alert, the names listed here will recieve a slack alert from a
|
||||
slackbot</p>
|
||||
<p>You must include a @ for direct message alerts and # for channel alerts</p>
|
||||
<p>For example, if the DevOps team wanted to get an alert in slack, the value in the
|
||||
text
|
||||
box would be <b>#devops</b>.
|
||||
If I wanted to also include a direct message as well then the value would be
|
||||
<b>#devops,@codyc</b></p>
|
||||
<p>Don't troll people with your metric alerts bombing peopls slack, it's unkind</p>
|
||||
<p>If the checkbox isn't selected, when generating the .yaml config the values
|
||||
listed will
|
||||
be ignored</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
{% if slack=="on" %}
|
||||
{% set slack_checked='checked' %}
|
||||
{% else %}
|
||||
{% set slack_disabled='disabled' %}
|
||||
{% endif %}
|
||||
<input type="checkbox" name="slack" id="slack" aria-label="..." onclick="dynEnable(this);" {{
|
||||
slack_checked }}>
|
||||
</span>
|
||||
<span id="insertinputs_slack"></span>
|
||||
<input type="text" name="slack_list" class="form-control" id="slack_list"
|
||||
value="{{ slack_list }}"
|
||||
aria-label="..." onkeyup="return forceComma(this);" {{ slack_disabled }}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3 class="text-center">Dashboard</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard URL -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="query">Dashboard URL:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#dashboardModal">
|
||||
info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="dashboardModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="dashboardTitle">Dashboard URL</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Most queries are built based on some dashboard already built in grafana</p>
|
||||
<p>By including the URL to that dashboard, the oncall engineer recieving the alert
|
||||
will be
|
||||
able to click the link in the alert and get a better picture of what this alert
|
||||
is and
|
||||
and how it relates to the datacenter</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<input type="text" name="url" id="url" class="form-control" value="{{ url }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3 class="text-center">Kairosdb Query</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KairosDB Query -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="query">KariosDB Query:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#queryModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="queryModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="queryTitle">KariosDB Query</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Paste in your KariosDB Query that you have already worked out.</p>
|
||||
<p>You can generate your query by going to the <a
|
||||
href="http://kairosdb-metrics.service.eng.consul:8080/" target="_blank">KariosDB
|
||||
UI
|
||||
in eng</a></p>
|
||||
<p>When generating your metric you will want to get the return values down to just 1
|
||||
or 2
|
||||
results per grouping. This can be done by sending the query to the MAX or MIN
|
||||
aggregators (depending on your logic needs) as the last aggregator in the
|
||||
query</p>
|
||||
<p>You will also want to include a time offset, typically 5 minutes is used for when
|
||||
to
|
||||
start (as from 5 minutes ago to now). Setting the MAX aggregator to this value
|
||||
is
|
||||
usually typical</p>
|
||||
<p>Once you have generated your query and it's returning the results you expect,
|
||||
click the
|
||||
<b>Show Query</b> button on the kairosDB UI and copy the results into this field
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<textarea name="kairosdb_query" id="kairosdb_query" class="form-control" rows="12" cols="50">{{ kairosdb_query }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3 class="text-center">Prometheus Query</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Prometheus URL -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label for="prometheus_url" class="control-label">Prometheus URL:</label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#prometheusurlModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="prometheusurlModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="myModalLabel">Prometheus URL</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>URL for the prometheus server</p>
|
||||
<p>Shared, production Prometheus URLs are currently:
|
||||
<ul>
|
||||
<li>http://big-trickster.service.eng.consul:9090</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input type="text" id="prometheus_url" class="form-control" name="prometheus_url" value="{{ prometheus_url }}"
|
||||
onkeyup="return forceLower(this);">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Prometheus Query -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="query">Prometheus Query:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#prometheusQueryModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="prometheusQueryModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="queryTitle">Prometheus Query</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Paste in your Prometheus Query that you have already worked out.</p>
|
||||
<p>You can generate your query by going to the url of your prometheus endpoint. Eng Vis plans on adding a smart router for this in the future so all instances will be exposed via a single smart proxy, but for now you'll need to know the name. </p><p><a
|
||||
href="http://big-trickster.service.eng.consul:9090/graph" target="_blank">Prometheus Host Metrics
|
||||
UI
|
||||
in eng</a>
|
||||
</p><p>
|
||||
<a
|
||||
href="http://big-trickster.service.eng.consul:9090/graph" target="_blank">Prometheus StatsD and other Metrics
|
||||
UI
|
||||
in eng</a></p>
|
||||
<p>When creating a query, keep in mind a single value returned is gonna be the most
|
||||
useful
|
||||
, so stuff like "topk(1,yourmetrics)" are gonna be good choices. However, if
|
||||
your query has multiple return values AOM will use last value.</p>
|
||||
<p>So if you use a step/duration of 60 and a timspan of 300 between start
|
||||
and
|
||||
end you'll get back 5 values and the last will be used.
|
||||
</p>
|
||||
<p><a href="https://prometheus.io/docs/prometheus/latest/querying/functions/" target="_blank">Prometheus Functions</a></p>
|
||||
<p>
|
||||
<a href="https://prometheus.io/docs/prometheus/latest/querying/operators/" target="_blank">Prometheus Operators</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7">
|
||||
<textarea name="prometheus_query" id="prometheus_query" class="form-control" rows="12" cols="50">{{ prometheus_query }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Start Time -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="start_time">Start Time: </label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#startTimeModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="startTimeModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="startTime">Start Time</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This should be a relative time in seconds like '-600' for 10m, defaults to '-300'</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<input type="text" id="start_time" class="form-control" name="start_time" value="{{ start_time }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- End Time -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="end_time">End Time: </label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#endTimeModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="endTimeModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="endTime">End Time</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This can be 'now' (default) or some relative offset like '-30' in seconds</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<input type="text" id="end_time" class="form-control" name="end_time" value="{{ end_time }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<h3 class="text-center">Actions</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load Config File -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="loadFile">Load Config From File:</label>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#loadModal">info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="loadModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="loadTitle">Load Config from file</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Load a config already generated to file into the UI</p>
|
||||
|
||||
<p>This is handy when you need to make minor changes to a query, or add additional
|
||||
alerting
|
||||
values or change thresholds. Or if you are just terrified of yaml.</p>
|
||||
<p>Hit the drop down to see a list of all alert configs (the names generated from
|
||||
the values
|
||||
used in the Alert Name field) Hit the Go and the config will load into all the
|
||||
fields</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="input-group">
|
||||
<select name="loadFile" id="loadFile" class="form-control">
|
||||
<option value="" selected></option>
|
||||
{% for f in alert_list %}
|
||||
<option value="{{ f }}">{{ f }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<span class="input-group-btn">
|
||||
<input type="submit" name="generate" id="submitFiles" class="btn btn-primary" value="Go">
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Submit Form -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4">
|
||||
<label class="control-label" for="submit">Generate YAML:</label>
|
||||
</div>
|
||||
<div class="col-sm-1">
|
||||
<!-- Button trigger modal -->
|
||||
<button type="button" class="btn btn-info btn-xs" data-toggle="modal" data-target="#generateModal">
|
||||
info
|
||||
</button>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="generateModal" tabindex="-1" role="dialog"
|
||||
aria-labelledby="myModalLabel">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="generateTitle">Generate Alert Config</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>When you are ready to take the values in the form and generate a alert config
|
||||
.yaml file,
|
||||
hit the button</p>
|
||||
<p>This will generate a .yaml file based on the alert name. So for example if one
|
||||
was to
|
||||
have the value <b>mcp_errors_per_dc</b> as an alert name, the resulting file
|
||||
would be
|
||||
<b>mcp_errors_per_dc.yaml</b></p>
|
||||
<p>This <b>will</b> overwrite a .yaml file if the alert name is the same as an
|
||||
already
|
||||
existing file</p>
|
||||
<p>If there are any errors generating the config, the resulting page will include
|
||||
the error
|
||||
message and give you the ability to return back to this page with your form
|
||||
saved</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<input type="submit" id="submit" name='generate' class='btn btn-primary' value="generate"
|
||||
class="button">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
4
AoM_Service/AoM_Configs/webapp/templates/layout.html
Executable file
4
AoM_Service/AoM_Configs/webapp/templates/layout.html
Executable file
@@ -0,0 +1,4 @@
|
||||
{% extends "header.html" %}
|
||||
{% block body %}
|
||||
<h2>Complete all values in the form below</h2>
|
||||
{% endblock %}
|
||||
69
AoM_Service/AoM_Configs/webapp/views.py
Executable file
69
AoM_Service/AoM_Configs/webapp/views.py
Executable file
@@ -0,0 +1,69 @@
|
||||
# views.py
|
||||
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
|
||||
import yaml
|
||||
from flask import session
|
||||
|
||||
from library.logger import AlertLogging
|
||||
from webapp import app, render_template, request, render
|
||||
|
||||
logger = AlertLogging('aom')
|
||||
logger.start()
|
||||
logger.start_log_file("logs/aom_service.log")
|
||||
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
logger.debug("Request Method: {}".format(request.method))
|
||||
if request.method == 'GET':
|
||||
# GET BLOB OF FILES
|
||||
service_config = yaml.load(open('service.yaml', 'r').read())
|
||||
alert_list = sorted([os.path.splitext(os.path.basename(x))[0] for x in
|
||||
glob.glob(service_config['alert_folder'] + "/*.yaml")])
|
||||
if 'yaml_config' in session:
|
||||
return render_template('index.html', **json.loads(session['yaml_config']), alert_list=alert_list)
|
||||
else:
|
||||
return render_template('index.html', alert_list=alert_list)
|
||||
elif request.method == 'POST':
|
||||
logger.info("Got a form")
|
||||
if 'go' in request.form['generate'].lower():
|
||||
return re_build(request.form['loadFile'])
|
||||
yaml_config = dict()
|
||||
ret = ''
|
||||
try:
|
||||
for field_name, value in request.form.items():
|
||||
yaml_config[field_name] = value
|
||||
code, ret = render.render_config(yaml_config)
|
||||
assert code == 0
|
||||
return render_template('debug.html', query=yaml_config,
|
||||
file_path='alert_configs/{}.yaml'.format(yaml_config['alert_name']),
|
||||
file_contents=ret.split('\n'))
|
||||
except AssertionError:
|
||||
session['yaml_config'] = json.dumps(yaml_config)
|
||||
return render_template('error.html', message="Failed to render to file: {}".format(ret))
|
||||
except Exception as e:
|
||||
return render_template('error.html', message=str(e))
|
||||
|
||||
|
||||
@app.route('/build/<alert_id>', methods=['POST'])
|
||||
def re_build(alert_id):
|
||||
# READ IN CONFIG FROM ID
|
||||
config = render.render_yaml(alert_id)
|
||||
service_config = yaml.load(open('service.yaml', 'r').read())
|
||||
alert_list = sorted([os.path.splitext(os.path.basename(x))[0] for x in
|
||||
glob.glob(service_config['alert_folder'] + "/*.yaml")])
|
||||
return render_template('index.html', **config, alert_list=alert_list)
|
||||
|
||||
|
||||
@app.route("/debug/")
|
||||
def toggle_debug():
|
||||
if logger.debug_handler:
|
||||
logger.stop_debug()
|
||||
logger.info("Debug Stopped")
|
||||
else:
|
||||
logger.start_debug()
|
||||
logger.debug("Debug Started")
|
||||
return index()
|
||||
Reference in New Issue
Block a user