Newer
Older
#!/usr/bin/python3
"""
// © University of Southampton IT Innovation Centre, 2018
//
// Copyright in this software belongs to University of Southampton
// IT Innovation Centre of Gamma House, Enterprise Road,
// Chilworth Science Park, Southampton, SO16 7NS, UK.
//
// This software may not be used, sold, licensed, transferred, copied
// or reproduced in whole or in part in any manner or form or in or
// on any media by any person other than in accordance with the terms
// of the Licence Agreement supplied with the software, or otherwise
// without the prior written consent of the copyright owners.
//
// This software is distributed WITHOUT ANY WARRANTY, without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE, except where stated in the Licence Agreement supplied with
// the software.
//
// Created By : Nikolay Stanchev
// Created Date : 16-08-2018
// Created for Project : FLAME
"""
# Python standard libs
from os.path import join
Nikolay Stanchev
committed
# PIP installed libs
from yaml import load
# CLMC-service imports
from clmcservice import ROOT_DIR
CLMC_ALERTS_TOSCA_DEFINITIONS_REL_PATH = ["static", "flame_clmc_alerts_definitions.yaml"]
CLMC_ALERTS_TOSCA_DEFINITIONS_ABS_PATH = join(ROOT_DIR, *CLMC_ALERTS_TOSCA_DEFINITIONS_REL_PATH)
CLMC_ALERTS_TOSCA_DEFINITIONS_FILE = CLMC_ALERTS_TOSCA_DEFINITIONS_REL_PATH[-1]
def adjust_tosca_definitions_import(alert_spec):
"""
A utility function to adjust any imports of flame_clmc_alerts_definitions.yaml to point to the correct location of
the tosca definitions file.
:param alert_spec: the TOSCA alert specification content (yaml dict)
"""
global CLMC_ALERTS_TOSCA_DEFINITIONS_ABS_PATH
try:
import_index = alert_spec["imports"].index(CLMC_ALERTS_TOSCA_DEFINITIONS_FILE)
alert_spec["imports"][import_index] = CLMC_ALERTS_TOSCA_DEFINITIONS_ABS_PATH
except Exception:
pass # nothing to replace if the import is not specified (either imports are missed, or no reference to the clmc tosca definitions file)
Nikolay Stanchev
committed
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def get_resource_spec_topic_ids(resource_spec_reference):
"""
Tries to extract all event identifiers from a TOSCA resource specification
:param resource_spec_reference: the resource specification file reference from the POST HTTP request
:return: sfc ID, sfc instance ID and the list of topic IDs
"""
resource_spec = load(resource_spec_reference.file)
topic_ids = []
sfc, sfc_i = resource_spec["metadata"]["sfc"], resource_spec["metadata"]["sfci"]
policies = resource_spec["topology_template"]["policies"]
for policy in policies:
policy = list(policy.items())[0]
policy_id, policy_object = policy[0], policy[1]
if policy_object["type"] == "eu.ict-flame.policies.StateChange":
triggers = policy_object["triggers"]
for trigger in triggers.values():
event = trigger["condition"]["constraint"]
source, event_id = event.split("::")
if source.lower() == "clmc": # only take those event IDs that have clmc set as their source
topic_ids.append("{0}\n{1}".format(policy_id, event_id))
return sfc, sfc_i, topic_ids
def get_alert_spec_topic_ids(alerts_spec_tpl):
"""
Tries to extract all event identifiers from a TOSCA alerts specification
:param alerts_spec_tpl: the alerts specification TOSCA template object
:return: the list of topic IDs
"""
topic_ids = []
for policy in alerts_spec_tpl.policies:
policy_id = policy.name
for trigger in policy.triggers:
trigger_id = trigger.name
topic_id = "{0}\n{1}".format(policy_id, trigger_id)
topic_ids.append(topic_id)
return topic_ids
def fill_http_post_handler_vars(handler_id, handler_url):
Creates a dictionary object ready to be posted to kapacitor to create an alert handler.
:param handler_id: handler identifier
:param handler_url: url to post alerts to
:return: a dictionary object ready to be posted to kapacitor to create an alert handler.
return {
"id": handler_id,
"kind": "post",
"options": {
"url": handler_url
}
}
class TICKScriptTemplateFiller:
"""
A utility class used for TICK script templates filtering.
"""
# a class variable used to hold the comparison operator used to build the where clause in TICK script templates,
# these differ if the where clause is built as a string opposed to when it is build as a lambda
_TEMPLATE_COMPARISON_OPERATOR = {"threshold": "=", "relative": "=", "deadman": "=="}
@staticmethod
def get_comparison_operator(template_type):
"""
Get the correct comparison operator depending on the template type, if template type not recognized, return "=="
:param template_type: one of the template types, that are created within kapacitor
:return: the comparison operator that should be used in the template to build the where clause
"""
return TICKScriptTemplateFiller._TEMPLATE_COMPARISON_OPERATOR.get(template_type, "==")
@staticmethod
def fill_template_vars(template_type, **kwargs):
"""
A utility function acting as an entry poiny to the fill_<template_type>_template_vars() functions defined below.
:param template_type: the template type - e.g.
:param kwargs: keyword arguments to forward to the actual function that will be used
:return: the result of the actual function that will be used.
"""
fill_function_name = "_fill_{0}_template_vars".format(template_type)
fill_function = getattr(TICKScriptTemplateFiller, fill_function_name) # python functions are first-class objects !
return fill_function(**kwargs)
@staticmethod
def _fill_threshold_template_vars(db=None, measurement=None, field=None, influx_function=None, critical_value=None,
comparison_operator=None, alert_period=None, topic_id=None, where_clause=None, **kwargs):
"""
Creates a dictionary object ready to be posted to kapacitor to create a "threshold" task from template.
:param db: db name
:param measurement: measurement name
:param field: field name
:param influx_function: influx function to use for querying
:param critical_value: critical value to compare with
:param comparison_operator: type of comparison
:param alert_period: alert period to query influx
:param topic_id: topic identifier
:param where_clause: (OPTIONAL) argument for filtering the influx query by tag values
:return: a dictionary object ready to be posted to kapacitor to create a "threshold" task from template.
"""
comparison_lambda = '"real_value" {0} {1}'.format(comparison_operator, critical_value) # build up lambda string, e.g. "real_value" >= 10
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
template_vars = {
"db": {
"type": "string",
"value": db
},
"measurement": {
"type": "string",
"value": measurement
},
"field": {
"type": "string",
"value": field
},
"influxFunction": {
"type": "string",
"value": influx_function
},
"comparisonLambda": {
"type": "lambda",
"value": comparison_lambda
},
"alertPeriod": {
"type": "duration",
"value": alert_period
},
"topicID": {
"type": "string",
"value": topic_id
}
}
if where_clause is not None:
template_vars["whereClause"] = {
"type": "string",
"value": where_clause
}
return template_vars
@staticmethod
def _fill_relative_template_vars(db=None, measurement=None, field=None, influx_function=None, critical_value=None, comparison_operator=None,
alert_period=None, topic_id=None, where_clause=None, **kwargs):
"""
Creates a dictionary object ready to be posted to kapacitor to create a "relative" task from template.
:param db: db name
:param measurement: measurement name
:param field: field name
:param influx_function: influx function to use for querying
:param critical_value: critical value to compare with
:param comparison_operator: type of comparison
:param alert_period: alert period to use for relative comparison
:param topic_id: topic identifier
:param where_clause: (OPTIONAL) argument for filtering the influx query by tag values
:return: a dictionary object ready to be posted to kapacitor to create a "relative" task from template.
"""
comparison_lambda = '"diff" {0} {1}'.format(comparison_operator, critical_value)
template_vars = {
"db": {
"type": "string",
"value": db
},
"measurement": {
"type": "string",
"value": measurement
},
"field": {
"type": "string",
"value": field
},
"influxFunction": {
"type": "string",
"value": influx_function
},
"comparisonLambda": {
"type": "lambda",
"value": comparison_lambda
},
"alertPeriod": {
"type": "duration",
"value": alert_period
},
"topicID": {
"type": "string",
"value": topic_id
}
}
if where_clause is not None:
template_vars["whereClause"] = {
"value": where_clause
}
return template_vars
@staticmethod
def _fill_deadman_template_vars(db=None, measurement=None, critical_value=None, alert_period=None, topic_id=None, where_clause=None, **kwargs):
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
"""
Creates a dictionary object ready to be posted to kapacitor to create a "deadman" task from template.
:param db: db name
:param measurement: measurement name
:param critical_value: critical value to compare with
:param alert_period: alert period to use for relative comparison
:param topic_id: topic identifier
:param where_clause: (OPTIONAL) argument for filtering the influx query by tag values
:return: a dictionary object ready to be posted to kapacitor to create a "deadman" task from template.
"""
template_vars = {
"db": {
"type": "string",
"value": db
},
"measurement": {
"type": "string",
"value": measurement
},
"alertPeriod": {
"type": "duration",
"value": alert_period
},
"throughputThreshold": {
"value": critical_value
},
"topicID": {
"type": "string",
"value": topic_id
}
}
if where_clause is not None:
template_vars["whereClause"] = {
"type": "lambda",
"value": where_clause
}
return template_vars