From 66ad59ab4484083a4a05e30829960a74d521233f Mon Sep 17 00:00:00 2001
From: Nikolay Stanchev <ns17@it-innovation.soton.ac.uk>
Date: Thu, 4 Oct 2018 11:45:32 +0100
Subject: [PATCH] Updates tick script templates and alerts API to include
 sfc,sfci,policy in JSON body

---
 .../alertsapi/alerts_specification_schema.py  |  2 +-
 .../clmcservice/alertsapi/utilities.py        | 56 +++++++++++++++++--
 src/service/clmcservice/alertsapi/views.py    |  6 +-
 .../TICKscript/deadman-template.tick          |  2 +-
 .../TICKscript/relative-stream-template.tick  | 46 ---------------
 .../TICKscript/relative-template.tick         |  2 +-
 .../TICKscript/threshold-batch-template.tick  |  2 +-
 .../TICKscript/threshold-stream-template.tick |  2 +-
 src/test/clmctest/alerts/test_alerts.py       |  6 +-
 9 files changed, 65 insertions(+), 59 deletions(-)
 delete mode 100644 src/service/resources/TICKscript/relative-stream-template.tick

diff --git a/src/service/clmcservice/alertsapi/alerts_specification_schema.py b/src/service/clmcservice/alertsapi/alerts_specification_schema.py
index bc3a985..8eaa2d7 100644
--- a/src/service/clmcservice/alertsapi/alerts_specification_schema.py
+++ b/src/service/clmcservice/alertsapi/alerts_specification_schema.py
@@ -78,7 +78,7 @@ ALERTS_SPECIFICATION_SCHEMA = Schema({
         "policies": [
             {
                 str: {
-                    "type": Or("eu.ict-flame.policies.StateChange", "eu.ict-flame.policies.Alert"),
+                    "type": "eu.ict-flame.policies.StateChange",
                     "triggers": And({
                         str: {
                             Optional("description"): str,
diff --git a/src/service/clmcservice/alertsapi/utilities.py b/src/service/clmcservice/alertsapi/utilities.py
index eba22f2..55470c1 100644
--- a/src/service/clmcservice/alertsapi/utilities.py
+++ b/src/service/clmcservice/alertsapi/utilities.py
@@ -152,7 +152,7 @@ class TICKScriptTemplateFiller:
         return fill_function(**kwargs)
 
     @staticmethod
-    def _fill_threshold_batch_template_vars(db=None, measurement=None, field=None, influx_function=None, critical_value=None,
+    def _fill_threshold_batch_template_vars(sfc=None, sfci=None, policy=None, db=None, measurement=None, field=None, influx_function=None, critical_value=None,
                                             comparison_operator=None, alert_period=None, topic_id=None, event_id=None, where_clause=None, **kwargs):
         """
         Creates a dictionary object ready to be posted to kapacitor to create a "threshold" task from template.
@@ -174,6 +174,18 @@ class TICKScriptTemplateFiller:
         comparison_lambda = '"real_value" {0} {1}'.format(comparison_operator, critical_value)  # build up lambda string, e.g. "real_value" >= 10
 
         template_vars = {
+            "sfc": {
+                "type": "string",
+                "value": sfc
+            },
+            "sfci": {
+                "type": "string",
+                "value": sfci
+            },
+            "policy": {
+                "type": "string",
+                "value": policy
+            },
             "db": {
                 "type": "string",
                 "value": db
@@ -217,7 +229,7 @@ class TICKScriptTemplateFiller:
         return template_vars
 
     @staticmethod
-    def _fill_threshold_stream_template_vars(db=None, measurement=None, field=None, critical_value=None,
+    def _fill_threshold_stream_template_vars(sfc=None, sfci=None, policy=None, db=None, measurement=None, field=None, critical_value=None,
                                              comparison_operator=None, topic_id=None, event_id=None, where_clause=None, **kwargs):
         """
         Creates a dictionary object ready to be posted to kapacitor to create a "threshold" task from template.
@@ -239,6 +251,18 @@ class TICKScriptTemplateFiller:
         comparison_lambda = '"{0}" {1} {2}'.format(field, comparison_operator, critical_value)  # build up lambda string, e.g. "real_value" >= 10
 
         template_vars = {
+            "sfc": {
+                "type": "string",
+                "value": sfc
+            },
+            "sfci": {
+                "type": "string",
+                "value": sfci
+            },
+            "policy": {
+                "type": "string",
+                "value": policy
+            },
             "db": {
                 "type": "string",
                 "value": db
@@ -271,7 +295,7 @@ class TICKScriptTemplateFiller:
         return template_vars
 
     @staticmethod
-    def _fill_relative_template_vars(db=None, measurement=None, field=None, influx_function=None, critical_value=None, comparison_operator=None,
+    def _fill_relative_template_vars(sfc=None, sfci=None, policy=None, db=None, measurement=None, field=None, influx_function=None, critical_value=None, comparison_operator=None,
                                      alert_period=None, topic_id=None, event_id=None, where_clause=None, **kwargs):
         """
         Creates a dictionary object ready to be posted to kapacitor to create a "relative" task from template.
@@ -293,6 +317,18 @@ class TICKScriptTemplateFiller:
         comparison_lambda = '"diff" {0} {1}'.format(comparison_operator, critical_value)
 
         template_vars = {
+            "sfc": {
+                "type": "string",
+                "value": sfc
+            },
+            "sfci": {
+                "type": "string",
+                "value": sfci
+            },
+            "policy": {
+                "type": "string",
+                "value": policy
+            },
             "db": {
                 "type": "string",
                 "value": db
@@ -336,7 +372,7 @@ class TICKScriptTemplateFiller:
         return template_vars
 
     @staticmethod
-    def _fill_deadman_template_vars(db=None, measurement=None, critical_value=None, alert_period=None, topic_id=None, event_id=None, where_clause=None, **kwargs):
+    def _fill_deadman_template_vars(sfc=None, sfci=None, policy=None, db=None, measurement=None, critical_value=None, alert_period=None, topic_id=None, event_id=None, where_clause=None, **kwargs):
         """
         Creates a dictionary object ready to be posted to kapacitor to create a "deadman" task from template.
 
@@ -352,6 +388,18 @@ class TICKScriptTemplateFiller:
         """
 
         template_vars = {
+            "sfc": {
+                "type": "string",
+                "value": sfc
+            },
+            "sfci": {
+                "type": "string",
+                "value": sfci
+            },
+            "policy": {
+                "type": "string",
+                "value": policy
+            },
             "db": {
                 "type": "string",
                 "value": db
diff --git a/src/service/clmcservice/alertsapi/views.py b/src/service/clmcservice/alertsapi/views.py
index ba07e26..5c586e3 100644
--- a/src/service/clmcservice/alertsapi/views.py
+++ b/src/service/clmcservice/alertsapi/views.py
@@ -251,9 +251,9 @@ class AlertsConfigurationAPI(object):
 
                 # built up the template vars dictionary depending on the event type (threshold, relative, etc.)
                 # all extracted properties from the trigger are passed, the TICKScriptTemplateFiller entry point then forwards those to the appropriate function for template filling
-                template_vars = TICKScriptTemplateFiller.fill_template_vars(event_type, db=db, measurement=measurement, field=field, influx_function=influx_function,
-                                                                            critical_value=critical_value, comparison_operator=comparison_operator, alert_period=alert_period,
-                                                                            topic_id=topic_id, event_id=event_id, where_clause=where_clause)
+                template_vars = TICKScriptTemplateFiller.fill_template_vars(event_type, sfc=sfc, sfci=sfc_instance, policy=policy_id, db=db, measurement=measurement,
+                                                                            field=field, influx_function=influx_function, critical_value=critical_value, comparison_operator=comparison_operator,
+                                                                            alert_period=alert_period, topic_id=topic_id, event_id=event_id, where_clause=where_clause)
 
                 # create and activate alert task through the kapacitor HTTP API
                 kapacitor_api_tasks_url = "http://{0}:{1}/kapacitor/v1/tasks".format(kapacitor_host, kapacitor_port)
diff --git a/src/service/resources/TICKscript/deadman-template.tick b/src/service/resources/TICKscript/deadman-template.tick
index 1b03fdb..730a461 100644
--- a/src/service/resources/TICKscript/deadman-template.tick
+++ b/src/service/resources/TICKscript/deadman-template.tick
@@ -31,7 +31,7 @@ stream
         .where(whereClause)
     | deadman(throughputThreshold, alertPeriod)
         .id(eventID)
-        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy + ',trigger=' + eventID)
+        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy)
         .message(messageValue)
         .topic(topicID)
         .noRecoveries()
\ No newline at end of file
diff --git a/src/service/resources/TICKscript/relative-stream-template.tick b/src/service/resources/TICKscript/relative-stream-template.tick
deleted file mode 100644
index 0a90f3d..0000000
--- a/src/service/resources/TICKscript/relative-stream-template.tick
+++ /dev/null
@@ -1,46 +0,0 @@
-var db string  // database per service function chain, so db is named after sfc
-
-var rp = 'autogen'  // default value for the retention policy
-
-var measurement string
-
-var selectLambda lambda  // must be a lambda specifying the field to select e.g. "requests"
-
-var whereClause = lambda: True  // default value is a function which returns TRUE, hence no filtering of the query result
-
-var messageValue = 'TRUE'  // default value is TRUE, as this is what SFEMC expects as a notification for an event rule
-
-var comparisonLambda lambda  // comparison function e.g. "diff" > 40
-
-var alertPeriod duration
-
-var topicID string
-
-
-var data = stream
-    | from()
-        .database(db)
-        .retentionPolicy(rp)
-        .measurement(measurement)
-        .where(whereClause)
-    | eval(selectLambda)
-        .as('value')
-
-var past = data
-   | shift(alertPeriod)
-
-var current = data
-
-past
-    | join(current)  // NOTE: join buffers a given data point until a point with the correct timestamp to join on arrives
-        .as('past', 'current')
-    | eval(lambda: float("current.value" - "past.value"))
-        .keep()
-        .as('diff')
-    | alert()
-        .id(topicID)
-        .details('db=' + db + ',measurement=' + measurement)
-        .crit(comparisonLambda)
-        .message(messageValue)
-        .topic(topicID)
-        .noRecoveries()
diff --git a/src/service/resources/TICKscript/relative-template.tick b/src/service/resources/TICKscript/relative-template.tick
index 9fb0daa..669954b 100644
--- a/src/service/resources/TICKscript/relative-template.tick
+++ b/src/service/resources/TICKscript/relative-template.tick
@@ -48,7 +48,7 @@ past
         .as('diff')
     | alert()
         .id(eventID)
-        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy + ',trigger=' + eventID)
+        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy)
         .crit(comparisonLambda)
         .message(messageValue)
         .topic(topicID)
diff --git a/src/service/resources/TICKscript/threshold-batch-template.tick b/src/service/resources/TICKscript/threshold-batch-template.tick
index f33f850..f5f7a64 100644
--- a/src/service/resources/TICKscript/threshold-batch-template.tick
+++ b/src/service/resources/TICKscript/threshold-batch-template.tick
@@ -32,7 +32,7 @@ batch
         .every(alertPeriod)
     |alert()
         .id(eventID)
-        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy + ',trigger=' + eventID)
+        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy)
         .crit(comparisonLambda)
         .message(messageValue)
         .topic(topicID)
diff --git a/src/service/resources/TICKscript/threshold-stream-template.tick b/src/service/resources/TICKscript/threshold-stream-template.tick
index fb85aba..9eb8865 100644
--- a/src/service/resources/TICKscript/threshold-stream-template.tick
+++ b/src/service/resources/TICKscript/threshold-stream-template.tick
@@ -29,7 +29,7 @@ stream
         .where(whereClause)
     | alert()
         .id(eventID)
-        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy + ',trigger=' + eventID)
+        .details('db=' + db + ',sfc=' + sfc + ',sfci=' + sfci + ',policy=' + policy)
         .crit(comparisonLambda)
         .message(messageValue)
         .topic(topicID)
diff --git a/src/test/clmctest/alerts/test_alerts.py b/src/test/clmctest/alerts/test_alerts.py
index ceb844e..a8679b3 100644
--- a/src/test/clmctest/alerts/test_alerts.py
+++ b/src/test/clmctest/alerts/test_alerts.py
@@ -44,7 +44,11 @@ def is_valid_timestamp(str_timestamp):
 
 
 def is_valid_details_string(details):
-    return "db=" in details and "sfc=" in details and "sfci=" in details and "policy" in details
+    try:
+        details_dict = {key.strip(): value.strip() for key,value in [item.split("=") for item in details.split(",")]}
+        return len(details_dict) == 4 and "db" in details_dict and "sfc" in details_dict and "sfci" in details_dict and "policy" in details_dict
+    except Exception:
+        return False
 
 
 JSON_BODY_SCHEMA = Schema({
-- 
GitLab