use configure file rather than arguments to organize the configuration
[releng.git] / utils / test / scripts / create_kibana_dashboards.py
index 252ce21..19d5b5e 100644 (file)
 #! /usr/bin/env python
-import logging
-import argparse
-import shared_utils
 import json
 import urlparse
 
-logger = logging.getLogger('create_kibana_dashboards')
-logger.setLevel(logging.DEBUG)
-file_handler = logging.FileHandler('/var/log/{}.log'.format(__name__))
-file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s'))
-logger.addHandler(file_handler)
+import argparse
 
-_installers = {'fuel', 'apex', 'compass', 'joid'}
+import logger_utils
+import shared_utils
+import testcases_parser
+from config import APIConfig
+
+logger = logger_utils.KibanaDashboardLogger('elastic2kibana').get
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-c", "--config-file",
+                    dest='config_file',
+                    help="Config file location")
 
-# see class VisualizationState for details on format
-_testcases = [
-    ('functest', 'Tempest',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "Tempest duration",
-                 "test_family": "VIM"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.tests"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.failures"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "Tempest nr of tests/failures",
-                 "test_family": "VIM"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.success_percentage"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "Tempest success percentage",
-                 "test_family": "VIM"
-             }
-         }
-     ]
-     ),
-
-    ('functest', 'Rally',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "Rally duration",
-                 "test_family": "VIM"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.tests"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "Rally nr of tests",
-                 "test_family": "VIM"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.success_percentage"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "Rally success percentage",
-                 "test_family": "VIM"
-             }
-         }
-     ]
-     ),
-
-    ('functest', 'vPing',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "vPing duration",
-                 "test_family": "VIM"
-             }
-         }
-     ]
-     ),
-
-    ('functest', 'vPing_userdata',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "vPing_userdata duration",
-                 "test_family": "VIM"
-             }
-         }
-     ]
-     ),
-
-    ('functest', 'ODL',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.tests"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.failures"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "ODL nr of tests/failures",
-                 "test_family": "Controller"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.success_percentage"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "ODL success percentage",
-                 "test_family": "Controller"
-             }
-         }
-     ]
-     ),
-
-    ('functest', 'ONOS',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.FUNCvirNet.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "ONOS FUNCvirNet duration",
-                 "test_family": "Controller"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.FUNCvirNet.tests"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.FUNCvirNet.failures"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "ONOS FUNCvirNet nr of tests/failures",
-                 "test_family": "Controller"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.FUNCvirNetL3.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "ONOS FUNCvirNetL3 duration",
-                 "test_family": "Controller"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.FUNCvirNetL3.tests"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.FUNCvirNetL3.failures"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "ONOS FUNCvirNetL3 nr of tests/failures",
-                 "test_family": "Controller"
-             }
-         }
-     ]
-     ),
-
-    ('functest', 'vIMS',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.sig_test.tests"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.sig_test.failures"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.sig_test.passed"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.sig_test.skipped"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "vIMS nr of tests/failures/passed/skipped",
-                 "test_family": "Features"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.vIMS.duration"
-                     }
-                 },
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.orchestrator.duration"
-                     }
-                 },
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.sig_test.duration"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "vIMS/ochestrator/test duration",
-                 "test_family": "Features"
-             }
-         }
-     ]
-     ),
-
-    ('promise', 'promise',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "promise duration",
-                 "test_family": "Features"
-             }
-         },
-
-         {
-             "metrics": [
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.tests"
-                     }
-                 },
-                 {
-                     "type": "sum",
-                     "params": {
-                         "field": "details.failures"
-                     }
-                 }
-             ],
-             "type": "histogram",
-             "metadata": {
-                 "label": "promise nr of tests/failures",
-                 "test_family": "Features"
-             }
-         }
-     ]
-     ),
-
-    ('doctor', 'doctor-notification',
-     [
-         {
-             "metrics": [
-                 {
-                     "type": "avg",
-                     "params": {
-                         "field": "details.duration"
-                     }
-                 }
-             ],
-             "type": "line",
-             "metadata": {
-                 "label": "doctor-notification duration",
-                 "test_family": "Features"
-             }
-         }
-     ]
-     )
-]
+args = parser.parse_args()
+CONF = APIConfig().parse(args.config_file)
+
+_installers = {'fuel', 'apex', 'compass', 'joid'}
 
 
 class KibanaDashboard(dict):
-    def __init__(self, project_name, case_name, installer, pod, versions, visualization_detail):
+    def __init__(self, project_name, case_name, family, installer, pod, scenarios, visualization):
         super(KibanaDashboard, self).__init__()
         self.project_name = project_name
         self.case_name = case_name
+        self.family = family
         self.installer = installer
         self.pod = pod
-        self.versions = versions
-        self.visualization_detail = visualization_detail
+        self.scenarios = scenarios
+        self.visualization = visualization
         self._visualization_title = None
         self._kibana_visualizations = []
         self._kibana_dashboard = None
@@ -433,13 +39,13 @@ class KibanaDashboard(dict):
         self._create()
 
     def _create_visualizations(self):
-        for version in self.versions:
+        for scenario in self.scenarios:
             self._kibana_visualizations.append(KibanaVisualization(self.project_name,
                                                                    self.case_name,
                                                                    self.installer,
                                                                    self.pod,
-                                                                   version,
-                                                                   self.visualization_detail))
+                                                                   scenario,
+                                                                   self.visualization))
 
         self._visualization_title = self._kibana_visualizations[0].vis_state_title
 
@@ -447,7 +53,7 @@ class KibanaDashboard(dict):
         for visualization in self._kibana_visualizations:
             url = urlparse.urljoin(base_elastic_url, '/.kibana/visualization/{}'.format(visualization.id))
             logger.debug("publishing visualization '{}'".format(url))
-            shared_utils.publish_json(visualization, es_user, es_passwd, url)
+            shared_utils.publish_json(visualization, es_creds, url)
 
     def _construct_panels(self):
         size_x = 6
@@ -495,7 +101,7 @@ class KibanaDashboard(dict):
         },
             separators=(',', ':'))
         self['uiStateJSON'] = "{}"
-        self['version'] = 1
+        self['scenario'] = 1
         self['timeRestore'] = False
         self['kibanaSavedObjectMeta'] = {
             'searchSourceJSON': json.dumps({
@@ -512,12 +118,20 @@ class KibanaDashboard(dict):
             },
                 separators=(',', ':'))
         }
-        self['metadata'] = self.visualization_detail['metadata']
+
+        label = self.case_name
+        if 'label' in self.visualization:
+            label += " %s" % self.visualization.get('label')
+        label += " %s" % self.visualization.get('name')
+        self['metadata'] = {
+            "label": label,
+            "test_family": self.family
+        }
 
     def _publish(self):
         url = urlparse.urljoin(base_elastic_url, '/.kibana/dashboard/{}'.format(self.id))
         logger.debug("publishing dashboard '{}'".format(url))
-        shared_utils.publish_json(self, es_user, es_passwd, url)
+        shared_utils.publish_json(self, es_creds, url)
 
     def publish(self):
         self._publish_visualizations()
@@ -533,71 +147,34 @@ class KibanaSearchSourceJSON(dict):
                 ]
     """
 
-    def __init__(self, project_name, case_name, installer, pod, version):
+    def __init__(self, project_name, case_name, installer, pod, scenario):
         super(KibanaSearchSourceJSON, self).__init__()
         self["filter"] = [
             {"match": {"project_name": {"query": project_name, "type": "phrase"}}},
             {"match": {"case_name": {"query": case_name, "type": "phrase"}}},
             {"match": {"installer": {"query": installer, "type": "phrase"}}},
-            {"match": {"version": {"query": version, "type": "phrase"}}}
+            {"match": {"scenario": {"query": scenario, "type": "phrase"}}}
         ]
         if pod != 'all':
             self["filter"].append({"match": {"pod_name": {"query": pod, "type": "phrase"}}})
 
 
 class VisualizationState(dict):
-    def __init__(self, input_dict):
-        """
-        dict structure:
-            {
-            "metrics":
-                [
-                    {
-                        "type": type,           # default sum
-                        "params": {
-                            "field": field      # mandatory, no default
-                    },
-                    {metric2}
-                ],
-            "segments":
-                [
-                    {
-                        "type": type,           # default date_histogram
-                        "params": {
-                            "field": field      # default creation_date
-                    },
-                    {segment2}
-                ],
-            "type": type,                       # default area
-            "mode": mode,                       # default grouped for type 'histogram', stacked for other types
-            "metadata": {
-                    "label": "Tempest duration",# mandatory, no default
-                    "test_family": "VIM"        # mandatory, no default
-                }
-            }
-
-        default modes:
-            type histogram: grouped
-            type area: stacked
-
-        :param input_dict:
-        :return:
-        """
+    def __init__(self, visualization):
         super(VisualizationState, self).__init__()
-        metrics = input_dict['metrics']
-        segments = [] if 'segments' not in input_dict else input_dict['segments']
-
-        graph_type = 'area' if 'type' not in input_dict else input_dict['type']
-        self['type'] = graph_type
-
-        if 'mode' not in input_dict:
-            if graph_type == 'histogram':
-                mode = 'grouped'
-            else:
-                # default
-                mode = 'stacked'
+        name = visualization.get('name')
+        fields = visualization.get('fields')
+
+        if name == 'tests_failures':
+            mode = 'grouped'
+            metric_type = 'sum'
+            self['type'] = 'histogram'
         else:
-            mode = input_dict['mode']
+            # duration or success_percentage
+            mode = 'stacked'
+            metric_type = 'avg'
+            self['type'] = 'line'
+
         self['params'] = {
             "shareYAxis": True,
             "addTooltip": True,
@@ -616,40 +193,23 @@ class VisualizationState(dict):
         self['aggs'] = []
 
         i = 1
-        for metric in metrics:
+        for field in fields:
             self['aggs'].append({
                 "id": str(i),
-                "type": 'sum' if 'type' not in metric else metric['type'],
+                "type": metric_type,
                 "schema": "metric",
                 "params": {
-                    "field": metric['params']['field']
+                    "field": field.get('field')
                 }
             })
             i += 1
 
-        if len(segments) > 0:
-            for segment in segments:
-                self['aggs'].append({
-                    "id": str(i),
-                    "type": 'date_histogram' if 'type' not in segment else segment['type'],
-                    "schema": "metric",
-                    "params": {
-                        "field": "creation_date" if ('params' not in segment or 'field' not in segment['params'])
-                        else segment['params']['field'],
-                        "interval": "auto",
-                        "customInterval": "2h",
-                        "min_doc_count": 1,
-                        "extended_bounds": {}
-                    }
-                })
-                i += 1
-        else:
-            self['aggs'].append({
+        self['aggs'].append({
                 "id": str(i),
                 "type": 'date_histogram',
                 "schema": "segment",
                 "params": {
-                    "field": "creation_date",
+                    "field": "start_date",
                     "interval": "auto",
                     "customInterval": "2h",
                     "min_doc_count": 1,
@@ -663,7 +223,7 @@ class VisualizationState(dict):
 
 
 class KibanaVisualization(dict):
-    def __init__(self, project_name, case_name, installer, pod, version, detail):
+    def __init__(self, project_name, case_name, installer, pod, scenario, visualization):
         """
         We need two things
         1. filter created from
@@ -671,7 +231,7 @@ class KibanaVisualization(dict):
             case_name
             installer
             pod
-            version
+            scenario
         2. visualization state
             field for y axis (metric) with type (avg, sum, etc.)
             field for x axis (segment) with type (date_histogram)
@@ -679,34 +239,34 @@ class KibanaVisualization(dict):
         :return:
         """
         super(KibanaVisualization, self).__init__()
-        vis_state = VisualizationState(detail)
+        vis_state = VisualizationState(visualization)
         self.vis_state_title = vis_state['title']
         self['title'] = '{} {} {} {} {} {}'.format(project_name,
                                                    case_name,
                                                    self.vis_state_title,
                                                    installer,
                                                    pod,
-                                                   version)
+                                                   scenario)
         self.id = self['title'].replace(' ', '-').replace('/', '-')
         self['visState'] = json.dumps(vis_state, separators=(',', ':'))
         self['uiStateJSON'] = "{}"
         self['description'] = "Kibana visualization for project_name '{}', case_name '{}', data '{}', installer '{}'," \
-                              " pod '{}' and version '{}'".format(project_name,
+                              " pod '{}' and scenario '{}'".format(project_name,
                                                                   case_name,
                                                                   self.vis_state_title,
                                                                   installer,
                                                                   pod,
-                                                                  version)
-        self['version'] = 1
+                                                                  scenario)
+        self['scenario'] = 1
         self['kibanaSavedObjectMeta'] = {"searchSourceJSON": json.dumps(KibanaSearchSourceJSON(project_name,
                                                                                                case_name,
                                                                                                installer,
                                                                                                pod,
-                                                                                               version),
+                                                                                               scenario),
                                                                         separators=(',', ':'))}
 
 
-def _get_pods_and_versions(project_name, case_name, installer):
+def _get_pods_and_scenarios(project_name, case_name, installer):
     query_json = json.JSONEncoder().encode({
         "query": {
             "bool": {
@@ -722,43 +282,52 @@ def _get_pods_and_versions(project_name, case_name, installer):
         }
     })
 
-    elastic_data = shared_utils.get_elastic_data(urlparse.urljoin(base_elastic_url, '/test_results/mongo2elastic'),
-                                                 es_user, es_passwd, query_json)
+    elastic_data = shared_utils.get_elastic_docs(urlparse.urljoin(base_elastic_url, '/test_results/mongo2elastic'),
+                                                 es_creds, query_json)
 
-    pods_and_versions = {}
+    pods_and_scenarios = {}
 
     for data in elastic_data:
         pod = data['pod_name']
-        if pod in pods_and_versions:
-            pods_and_versions[pod].add(data['version'])
+        if pod in pods_and_scenarios:
+            pods_and_scenarios[pod].add(data['scenario'])
         else:
-            pods_and_versions[pod] = {data['version']}
+            pods_and_scenarios[pod] = {data['scenario']}
 
-        if 'all' in pods_and_versions:
-            pods_and_versions['all'].add(data['version'])
+        if 'all' in pods_and_scenarios:
+            pods_and_scenarios['all'].add(data['scenario'])
         else:
-            pods_and_versions['all'] = {data['version']}
+            pods_and_scenarios['all'] = {data['scenario']}
 
-    return pods_and_versions
+    return pods_and_scenarios
 
 
 def construct_dashboards():
     """
     iterate over testcase and installer
     1. get available pods for each testcase/installer pair
-    2. get available version for each testcase/installer/pod tuple
+    2. get available scenario for each testcase/installer/pod tuple
     3. construct KibanaInput and append
 
     :return: list of KibanaDashboards
     """
     kibana_dashboards = []
-    for project_name, case_name, visualization_details in _testcases:
-        for installer in _installers:
-            pods_and_versions = _get_pods_and_versions(project_name, case_name, installer)
-            for visualization_detail in visualization_details:
-                for pod, versions in pods_and_versions.iteritems():
-                    kibana_dashboards.append(KibanaDashboard(project_name, case_name, installer, pod, versions,
-                                                             visualization_detail))
+    for project, case_dicts in testcases_parser.testcases_yaml.items():
+        for case in case_dicts:
+            case_name = case.get('name')
+            visualizations = case.get('visualizations')
+            family = case.get('test_family')
+            for installer in _installers:
+                pods_and_scenarios = _get_pods_and_scenarios(project, case_name, installer)
+                for visualization in visualizations:
+                    for pod, scenarios in pods_and_scenarios.iteritems():
+                        kibana_dashboards.append(KibanaDashboard(project,
+                                                                 case_name,
+                                                                 family,
+                                                                 installer,
+                                                                 pod,
+                                                                 scenarios,
+                                                                 visualization))
     return kibana_dashboards
 
 
@@ -791,29 +360,11 @@ def generate_js_inputs(js_file_path, kibana_url, dashboards):
 
 
 if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description='Create Kibana dashboards from data in elasticsearch')
-    parser.add_argument('-e', '--elasticsearch-url', default='http://localhost:9200',
-                        help='the url of elasticsearch, defaults to http://localhost:9200')
-    parser.add_argument('-js', '--generate_js_inputs', action='store_true',
-                        help='Use this argument to generate javascript inputs for kibana landing page')
-    parser.add_argument('--js_path', default='/usr/share/nginx/html/kibana_dashboards/conf.js',
-                        help='Path of javascript file with inputs for kibana landing page')
-    parser.add_argument('-k', '--kibana_url', default='https://testresults.opnfv.org/kibana/app/kibana',
-                        help='The url of kibana for javascript inputs')
-
-    parser.add_argument('-u', '--elasticsearch-username',
-                        help='the username for elasticsearch')
-
-    parser.add_argument('-p', '--elasticsearch-password',
-                        help='the password for elasticsearch')
-
-    args = parser.parse_args()
-    base_elastic_url = args.elasticsearch_url
-    generate_inputs = args.generate_js_inputs
-    input_file_path = args.js_path
-    kibana_url = args.kibana_url
-    es_user = args.elasticsearch_username
-    es_passwd = args.elasticsearch_password
+    base_elastic_url = CONF.elastic_url
+    generate_inputs = CONF.is_js
+    input_file_path = CONF.js_path
+    kibana_url = CONF.kibana_url
+    es_creds = CONF.elastic_creds
 
     dashboards = construct_dashboards()