From e7b7bb64bf2f3f03980504d80f304e4859146ed2 Mon Sep 17 00:00:00 2001 From: SerenaFeng Date: Thu, 22 Sep 2016 16:15:58 +0800 Subject: rebuild directory structure of Kibana dashboard JIRA: FUNCTEST-465 Change-Id: Icecd350b2f67105c8aaa9d71fd76d24827515545 Signed-off-by: SerenaFeng --- utils/test/dashboard.tar.gz | Bin 0 -> 14627 bytes utils/test/dashboard/backup-db.sh | 33 ++ utils/test/dashboard/dashboard/__init__.py | 0 utils/test/dashboard/dashboard/common/__init__.py | 0 .../dashboard/dashboard/common/elastic_access.py | 64 ++++ .../dashboard/dashboard/common/logger_utils.py | 65 ++++ utils/test/dashboard/dashboard/conf/__init__.py | 0 utils/test/dashboard/dashboard/conf/config.py | 88 +++++ utils/test/dashboard/dashboard/conf/testcases.py | 24 ++ .../dashboard/dashboard/elastic2kibana/__init__.py | 0 .../dashboard/dashboard/elastic2kibana/main.py | 373 ++++++++++++++++++++ .../dashboard/dashboard/elastic2kibana_main.py | 4 + .../test/dashboard/dashboard/functest/__init__.py | 0 .../dashboard/dashboard/functest/testcases.yaml | 138 ++++++++ .../dashboard/dashboard/mongo2elastic/__init__.py | 0 .../dashboard/dashboard/mongo2elastic/format.py | 186 ++++++++++ .../test/dashboard/dashboard/mongo2elastic/main.py | 243 +++++++++++++ .../test/dashboard/dashboard/mongo2elastic_main.py | 4 + utils/test/dashboard/etc/config.ini | 14 + utils/test/dashboard/kibana_cleanup.py | 41 +++ utils/test/scripts/backup-db.sh | 33 -- utils/test/scripts/config.ini | 14 - utils/test/scripts/config.py | 88 ----- utils/test/scripts/create_kibana_dashboards.py | 375 --------------------- utils/test/scripts/kibana_cleanup.py | 41 --- utils/test/scripts/logger_utils.py | 65 ---- utils/test/scripts/mongo2elastic_format.py | 186 ---------- utils/test/scripts/mongo_to_elasticsearch.py | 248 -------------- utils/test/scripts/shared_utils.py | 64 ---- utils/test/scripts/testcases.yaml | 138 -------- utils/test/scripts/testcases_parser.py | 24 -- 31 files changed, 1277 insertions(+), 1276 deletions(-) create mode 100644 utils/test/dashboard.tar.gz create mode 100644 utils/test/dashboard/backup-db.sh create mode 100644 utils/test/dashboard/dashboard/__init__.py create mode 100644 utils/test/dashboard/dashboard/common/__init__.py create mode 100644 utils/test/dashboard/dashboard/common/elastic_access.py create mode 100644 utils/test/dashboard/dashboard/common/logger_utils.py create mode 100644 utils/test/dashboard/dashboard/conf/__init__.py create mode 100644 utils/test/dashboard/dashboard/conf/config.py create mode 100644 utils/test/dashboard/dashboard/conf/testcases.py create mode 100644 utils/test/dashboard/dashboard/elastic2kibana/__init__.py create mode 100644 utils/test/dashboard/dashboard/elastic2kibana/main.py create mode 100644 utils/test/dashboard/dashboard/elastic2kibana_main.py create mode 100644 utils/test/dashboard/dashboard/functest/__init__.py create mode 100644 utils/test/dashboard/dashboard/functest/testcases.yaml create mode 100644 utils/test/dashboard/dashboard/mongo2elastic/__init__.py create mode 100644 utils/test/dashboard/dashboard/mongo2elastic/format.py create mode 100644 utils/test/dashboard/dashboard/mongo2elastic/main.py create mode 100644 utils/test/dashboard/dashboard/mongo2elastic_main.py create mode 100644 utils/test/dashboard/etc/config.ini create mode 100644 utils/test/dashboard/kibana_cleanup.py delete mode 100644 utils/test/scripts/backup-db.sh delete mode 100644 utils/test/scripts/config.ini delete mode 100644 utils/test/scripts/config.py delete mode 100644 utils/test/scripts/create_kibana_dashboards.py delete mode 100644 utils/test/scripts/kibana_cleanup.py delete mode 100644 utils/test/scripts/logger_utils.py delete mode 100644 utils/test/scripts/mongo2elastic_format.py delete mode 100644 utils/test/scripts/mongo_to_elasticsearch.py delete mode 100644 utils/test/scripts/shared_utils.py delete mode 100644 utils/test/scripts/testcases.yaml delete mode 100644 utils/test/scripts/testcases_parser.py (limited to 'utils/test') diff --git a/utils/test/dashboard.tar.gz b/utils/test/dashboard.tar.gz new file mode 100644 index 000000000..ef85f90da Binary files /dev/null and b/utils/test/dashboard.tar.gz differ diff --git a/utils/test/dashboard/backup-db.sh b/utils/test/dashboard/backup-db.sh new file mode 100644 index 000000000..35c3fbe5a --- /dev/null +++ b/utils/test/dashboard/backup-db.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# SPDX-license-identifier: Apache-2.0 +############################################################################## +# Copyright (c) 2016 Orange and others. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +echo "Backup Test collection DB" +now=$(date +"%m_%d_%Y_%H_%M_%S") +echo $now +echo " ------------- " +TARGET_DIR=./$now +TEST_RESULT_DB_BACKUP="test_collection_db."$now".tar.gz" + +echo "Create Directory for backup" +mkdir -p $TARGET_DIR + +echo "Export results" +mongoexport --db test_results_collection -c results --out $TARGET_DIR/backup-results.json +echo "Export test cases" +mongoexport --db test_results_collection -c testcases --out $TARGET_DIR/backup-cases.json +echo "Export projects" +mongoexport --db test_results_collection -c projects --out $TARGET_DIR/backup-projects.json +echo "Export pods" +mongoexport --db test_results_collection -c pods --out $TARGET_DIR/backup-pod.json + +echo "Create tar.gz" +#tar -cvzf $TEST_RESULT_DB_BACKUP $TARGET_DIR + +echo "Delete temp directory" +#rm -Rf $TARGET_DIR diff --git a/utils/test/dashboard/dashboard/__init__.py b/utils/test/dashboard/dashboard/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/test/dashboard/dashboard/common/__init__.py b/utils/test/dashboard/dashboard/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/test/dashboard/dashboard/common/elastic_access.py b/utils/test/dashboard/dashboard/common/elastic_access.py new file mode 100644 index 000000000..e90a17fa3 --- /dev/null +++ b/utils/test/dashboard/dashboard/common/elastic_access.py @@ -0,0 +1,64 @@ +import json + +import urllib3 + +http = urllib3.PoolManager() + + +def delete_request(url, creds, body=None): + headers = urllib3.make_headers(basic_auth=creds) + http.request('DELETE', url, headers=headers, body=body) + + +def publish_json(json_ojb, creds, to): + json_dump = json.dumps(json_ojb) + if to == 'stdout': + print json_dump + return 200, None + else: + headers = urllib3.make_headers(basic_auth=creds) + result = http.request('POST', to, headers=headers, body=json_dump) + return result.status, result.data + + +def _get_nr_of_hits(elastic_json): + return elastic_json['hits']['total'] + + +def get_elastic_docs(elastic_url, creds, body=None, field = '_source'): + + # 1. get the number of results + headers = urllib3.make_headers(basic_auth=creds) + elastic_json = json.loads(http.request('GET', elastic_url + '/_search?size=0', headers=headers, body=body).data) + print elastic_json + nr_of_hits = _get_nr_of_hits(elastic_json) + + # 2. get all results + elastic_json = json.loads(http.request('GET', elastic_url + '/_search?size={}'.format(nr_of_hits), headers=headers, body=body).data) + + elastic_docs = [] + for hit in elastic_json['hits']['hits']: + elastic_docs.append(hit[field]) + return elastic_docs + + +def get_elastic_docs_by_days(elastic_url, creds, days): + if days == 0: + body = '''{ + "query": { + "match_all": {} + } + }''' + elif days > 0: + body = '''{{ + "query" : {{ + "range" : {{ + "start_date" : {{ + "gte" : "now-{}d" + }} + }} + }} + }}'''.format(days) + else: + raise Exception('Update days must be non-negative') + return get_elastic_docs(elastic_url, creds, body) diff --git a/utils/test/dashboard/dashboard/common/logger_utils.py b/utils/test/dashboard/dashboard/common/logger_utils.py new file mode 100644 index 000000000..183080810 --- /dev/null +++ b/utils/test/dashboard/dashboard/common/logger_utils.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# feng.xiaowei@zte.com.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Logging levels: +# Level Numeric value +# CRITICAL 50 +# ERROR 40 +# WARNING 30 +# INFO 20 +# DEBUG 10 +# NOTSET 0 +# +# Usage: +# import functest_logger as fl +# logger = fl.Logger("script_name").getLogger() +# logger.info("message to be shown with - INFO - ") +# logger.debug("message to be shown with - DEBUG -") + +import logging +import os + + +class Logger(object): + file_path = '/var/log' + formatter = logging.Formatter('%(asctime)s - %(name)s - ' + '%(levelname)s - %(message)s') + + def __init__(self, logger_name): + + IF_DEBUG = os.getenv('IF_DEBUG') + + self.logger_name = logger_name + self.logger = logging.getLogger(logger_name) + self.logger.propagate = 0 + self.logger.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + ch.setFormatter(self.formatter) + if IF_DEBUG is not None and IF_DEBUG.lower() == "true": + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + self.logger.addHandler(ch) + + hdlr = logging.FileHandler('%s/%s.log' % (self.file_path, logger_name)) + hdlr.setFormatter(self.formatter) + hdlr.setLevel(logging.DEBUG) + self.logger.addHandler(hdlr) + + @property + def get(self): + return self.logger + + +class DashboardLogger(Logger): + file_path = '/var/log/kibana_dashboard' + + def __init__(self, logger_name): + super(DashboardLogger, self).__init__(logger_name) + diff --git a/utils/test/dashboard/dashboard/conf/__init__.py b/utils/test/dashboard/dashboard/conf/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/test/dashboard/dashboard/conf/config.py b/utils/test/dashboard/dashboard/conf/config.py new file mode 100644 index 000000000..2e0f1cabb --- /dev/null +++ b/utils/test/dashboard/dashboard/conf/config.py @@ -0,0 +1,88 @@ +#! /usr/bin/env python + +from ConfigParser import SafeConfigParser, NoOptionError + + +class ParseError(Exception): + """ + Custom exception class for config file + """ + + def __init__(self, message): + self.msg = message + + def __str__(self): + return 'error parsing config file : %s' % self.msg + + +class APIConfig: + """ + The purpose of this class is to load values correctly from the config file. + Each key is declared as an attribute in __init__() and linked in parse() + """ + + def __init__(self): + self._default_config_location = "../etc/config.ini" + self.elastic_url = 'http://localhost:9200' + self.elastic_creds = None + self.destination = 'elasticsearch' + self.kibana_url = None + self.is_js = True + self.js_path = None + + def _get_str_parameter(self, section, param): + try: + return self._parser.get(section, param) + except NoOptionError: + raise ParseError("[%s.%s] parameter not found" % (section, param)) + + def _get_int_parameter(self, section, param): + try: + return int(self._get_str_parameter(section, param)) + except ValueError: + raise ParseError("[%s.%s] not an int" % (section, param)) + + def _get_bool_parameter(self, section, param): + result = self._get_str_parameter(section, param) + if str(result).lower() == 'true': + return True + if str(result).lower() == 'false': + return False + + raise ParseError( + "[%s.%s : %s] not a boolean" % (section, param, result)) + + @staticmethod + def parse(config_location=None): + obj = APIConfig() + + if config_location is None: + config_location = obj._default_config_location + + obj._parser = SafeConfigParser() + obj._parser.read(config_location) + if not obj._parser: + raise ParseError("%s not found" % config_location) + + # Linking attributes to keys from file with their sections + obj.elastic_url = obj._get_str_parameter("elastic", "url") + obj.elastic_creds = obj._get_str_parameter("elastic", "creds") + obj.destination = obj._get_str_parameter("output", "destination") + obj.kibana_url = obj._get_str_parameter("kibana", "url") + obj.is_js = obj._get_bool_parameter("kibana", "js") + obj.js_path = obj._get_str_parameter("kibana", "js_path") + + return obj + + def __str__(self): + return "elastic_url = %s \n" \ + "elastic_creds = %s \n" \ + "destination = %s \n" \ + "kibana_url = %s \n" \ + "is_js = %s \n" \ + "js_path = %s \n" % (self.elastic_url, + self.elastic_creds, + self.destination, + self.kibana_url, + self.is_js, + self.js_path) diff --git a/utils/test/dashboard/dashboard/conf/testcases.py b/utils/test/dashboard/dashboard/conf/testcases.py new file mode 100644 index 000000000..e120987dd --- /dev/null +++ b/utils/test/dashboard/dashboard/conf/testcases.py @@ -0,0 +1,24 @@ +import yaml + + +with open('./functest/testcases.yaml') as f: + testcases_yaml = yaml.safe_load(f) +f.close() + + +def compose_format(fmt): + return 'format_' + fmt.strip() + + +def get_format(project, case): + testcases = testcases_yaml.get(project) + if isinstance(testcases, list): + for case_dict in testcases: + if case_dict['name'] == case: + return compose_format(case_dict['format']) + return None + + +if __name__ == '__main__': + fmt = get_format('functest', 'vping_ssh') + print fmt diff --git a/utils/test/dashboard/dashboard/elastic2kibana/__init__.py b/utils/test/dashboard/dashboard/elastic2kibana/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/test/dashboard/dashboard/elastic2kibana/main.py b/utils/test/dashboard/dashboard/elastic2kibana/main.py new file mode 100644 index 000000000..c1cbc308e --- /dev/null +++ b/utils/test/dashboard/dashboard/elastic2kibana/main.py @@ -0,0 +1,373 @@ +#! /usr/bin/env python +import json +import urlparse + +import argparse + +from common import logger_utils, elastic_access +from conf import testcases +from conf.config import APIConfig + +logger = logger_utils.DashboardLogger('elastic2kibana').get + +parser = argparse.ArgumentParser() +parser.add_argument("-c", "--config-file", + dest='config_file', + help="Config file location") + +args = parser.parse_args() +CONF = APIConfig().parse(args.config_file) +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 + +_installers = {'fuel', 'apex', 'compass', 'joid'} + + +class KibanaDashboard(dict): + 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.scenarios = scenarios + self.visualization = visualization + self._visualization_title = None + self._kibana_visualizations = [] + self._kibana_dashboard = None + self._create_visualizations() + self._create() + + def _create_visualizations(self): + for scenario in self.scenarios: + self._kibana_visualizations.append(KibanaVisualization(self.project_name, + self.case_name, + self.installer, + self.pod, + scenario, + self.visualization)) + + self._visualization_title = self._kibana_visualizations[0].vis_state_title + + def _publish_visualizations(self): + for visualization in self._kibana_visualizations: + url = urlparse.urljoin(base_elastic_url, '/.kibana/visualization/{}'.format(visualization.id)) + logger.debug("publishing visualization '{}'".format(url)) + elastic_access.publish_json(visualization, es_creds, url) + + def _construct_panels(self): + size_x = 6 + size_y = 3 + max_columns = 7 + column = 1 + row = 1 + panel_index = 1 + panels_json = [] + for visualization in self._kibana_visualizations: + panels_json.append({ + "id": visualization.id, + "type": 'visualization', + "panelIndex": panel_index, + "size_x": size_x, + "size_y": size_y, + "col": column, + "row": row + }) + panel_index += 1 + column += size_x + if column > max_columns: + column = 1 + row += size_y + return json.dumps(panels_json, separators=(',', ':')) + + def _create(self): + self['title'] = '{} {} {} {} {}'.format(self.project_name, + self.case_name, + self.installer, + self._visualization_title, + self.pod) + self.id = self['title'].replace(' ', '-').replace('/', '-') + + self['hits'] = 0 + self['description'] = "Kibana dashboard for project_name '{}', case_name '{}', installer '{}', data '{}' and" \ + " pod '{}'".format(self.project_name, + self.case_name, + self.installer, + self._visualization_title, + self.pod) + self['panelsJSON'] = self._construct_panels() + self['optionsJSON'] = json.dumps({ + "darkTheme": False + }, + separators=(',', ':')) + self['uiStateJSON'] = "{}" + self['scenario'] = 1 + self['timeRestore'] = False + self['kibanaSavedObjectMeta'] = { + 'searchSourceJSON': json.dumps({ + "filter": [ + { + "query": { + "query_string": { + "query": "*", + "analyze_wildcard": True + } + } + } + ] + }, + separators=(',', ':')) + } + + 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)) + elastic_access.publish_json(self, es_creds, url) + + def publish(self): + self._publish_visualizations() + self._publish() + + +class KibanaSearchSourceJSON(dict): + """ + "filter": [ + {"match": {"installer": {"query": installer, "type": "phrase"}}}, + {"match": {"project_name": {"query": project_name, "type": "phrase"}}}, + {"match": {"case_name": {"query": case_name, "type": "phrase"}}} + ] + """ + + 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": {"scenario": {"query": scenario, "type": "phrase"}}} + ] + if pod != 'all': + self["filter"].append({"match": {"pod_name": {"query": pod, "type": "phrase"}}}) + + +class VisualizationState(dict): + def __init__(self, visualization): + super(VisualizationState, self).__init__() + name = visualization.get('name') + fields = visualization.get('fields') + + if name == 'tests_failures': + mode = 'grouped' + metric_type = 'sum' + self['type'] = 'histogram' + else: + # duration or success_percentage + mode = 'stacked' + metric_type = 'avg' + self['type'] = 'line' + + self['params'] = { + "shareYAxis": True, + "addTooltip": True, + "addLegend": True, + "smoothLines": False, + "scale": "linear", + "interpolate": "linear", + "mode": mode, + "times": [], + "addTimeMarker": False, + "defaultYExtents": False, + "setYExtents": False, + "yAxis": {} + } + + self['aggs'] = [] + + i = 1 + for field in fields: + self['aggs'].append({ + "id": str(i), + "type": metric_type, + "schema": "metric", + "params": { + "field": field.get('field') + } + }) + i += 1 + + self['aggs'].append({ + "id": str(i), + "type": 'date_histogram', + "schema": "segment", + "params": { + "field": "start_date", + "interval": "auto", + "customInterval": "2h", + "min_doc_count": 1, + "extended_bounds": {} + } + }) + + self['listeners'] = {} + self['title'] = ' '.join(['{} {}'.format(x['type'], x['params']['field']) for x in self['aggs'] + if x['schema'] == 'metric']) + + +class KibanaVisualization(dict): + def __init__(self, project_name, case_name, installer, pod, scenario, visualization): + """ + We need two things + 1. filter created from + project_name + case_name + installer + pod + scenario + 2. visualization state + field for y axis (metric) with type (avg, sum, etc.) + field for x axis (segment) with type (date_histogram) + + :return: + """ + super(KibanaVisualization, self).__init__() + vis_state = VisualizationState(visualization) + self.vis_state_title = vis_state['title'] + self['title'] = '{} {} {} {} {} {}'.format(project_name, + case_name, + self.vis_state_title, + installer, + pod, + 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 scenario '{}'".format(project_name, + case_name, + self.vis_state_title, + installer, + pod, + scenario) + self['scenario'] = 1 + self['kibanaSavedObjectMeta'] = {"searchSourceJSON": json.dumps(KibanaSearchSourceJSON(project_name, + case_name, + installer, + pod, + scenario), + separators=(',', ':'))} + + +def _get_pods_and_scenarios(project_name, case_name, installer): + query_json = json.JSONEncoder().encode({ + "query": { + "bool": { + "must": [ + {"match_all": {}} + ], + "filter": [ + {"match": {"installer": {"query": installer, "type": "phrase"}}}, + {"match": {"project_name": {"query": project_name, "type": "phrase"}}}, + {"match": {"case_name": {"query": case_name, "type": "phrase"}}} + ] + } + } + }) + + elastic_data = elastic_access.get_elastic_docs(urlparse.urljoin(base_elastic_url, '/test_results/mongo2elastic'), + es_creds, query_json) + + pods_and_scenarios = {} + + for data in elastic_data: + pod = data['pod_name'] + if pod in pods_and_scenarios: + pods_and_scenarios[pod].add(data['scenario']) + else: + pods_and_scenarios[pod] = {data['scenario']} + + if 'all' in pods_and_scenarios: + pods_and_scenarios['all'].add(data['scenario']) + else: + pods_and_scenarios['all'] = {data['scenario']} + + return pods_and_scenarios + + +def construct_dashboards(): + """ + iterate over testcase and installer + 1. get available pods for each testcase/installer pair + 2. get available scenario for each testcase/installer/pod tuple + 3. construct KibanaInput and append + + :return: list of KibanaDashboards + """ + kibana_dashboards = [] + for project, case_dicts in testcases.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 + + +def generate_js_inputs(js_file_path, kibana_url, dashboards): + js_dict = {} + for dashboard in dashboards: + dashboard_meta = dashboard['metadata'] + test_family = dashboard_meta['test_family'] + test_label = dashboard_meta['label'] + + if test_family not in js_dict: + js_dict[test_family] = {} + + js_test_family = js_dict[test_family] + + if test_label not in js_test_family: + js_test_family[test_label] = {} + + js_test_label = js_test_family[test_label] + + if dashboard.installer not in js_test_label: + js_test_label[dashboard.installer] = {} + + js_installer = js_test_label[dashboard.installer] + js_installer[dashboard.pod] = kibana_url + '#/dashboard/' + dashboard.id + + with open(js_file_path, 'w+') as js_file_fdesc: + js_file_fdesc.write('var kibana_dashboard_links = ') + js_file_fdesc.write(str(js_dict).replace("u'", "'")) + + +def main(): + dashboards = construct_dashboards() + + for kibana_dashboard in dashboards: + kibana_dashboard.publish() + + if generate_inputs: + generate_js_inputs(input_file_path, kibana_url, dashboards) diff --git a/utils/test/dashboard/dashboard/elastic2kibana_main.py b/utils/test/dashboard/dashboard/elastic2kibana_main.py new file mode 100644 index 000000000..3ec27cb40 --- /dev/null +++ b/utils/test/dashboard/dashboard/elastic2kibana_main.py @@ -0,0 +1,4 @@ +from elastic2kibana.main import main + +if __name__ == '__main__': + main() diff --git a/utils/test/dashboard/dashboard/functest/__init__.py b/utils/test/dashboard/dashboard/functest/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/test/dashboard/dashboard/functest/testcases.yaml b/utils/test/dashboard/dashboard/functest/testcases.yaml new file mode 100644 index 000000000..9c33d2e6b --- /dev/null +++ b/utils/test/dashboard/dashboard/functest/testcases.yaml @@ -0,0 +1,138 @@ +functest: + - + name: tempest_smoke_serial + format: normal + test_family: VIM + visualizations: + - + name: duration + fields: + - field: details.duration + - + name: tests_failures + fields: + - field: details.tests + - field: details.failures + - + name: success_percentage + fields: + - field: details.success_percentage + - + name: rally_sanity + test_family: VIM + format: rally + visualizations: + - + name: duration + fields: + - field: details.duration + - + name: tests_failures + fields: + - field: details.tests + - + name: success_percentage + fields: + - field: details.success_percentage + - + name: vping_ssh + format: normal + test_family: VIM + visualizations: + - + name: duration + fields: + - field: details.duration + - + name: vping_userdata + format: normal + test_family: VIM + visualizations: + - + name: duration + fields: + - field: details.duration + - + name: odl + test_family: Controller + format: odl + visualizations: + - + name: tests_failures + fields: + - field: details.tests + - field: details.failures + - + name: success_percentage + fields: + - field: details.success_percentage + - + name: onos + format: onos + test_family: Controller + visualizations: + - + name: duration + label: FUNCvirNet + fields: + - field: details.FUNCvirNet.duration + - + name: duration + label: FUNCvirNetL3 + fields: + - field: details.FUNCvirNetL3.duration + - + name: tests_failures + label: FUNCvirNet + fields: + - field: details.FUNCvirNet.tests + - field: details.FUNCvirNet.failures + - + name: tests_failures + label: FUNCvirNetL3 + fields: + - field: details.FUNCvirNetL3.tests + - field: details.FUNCvirNetL3.failures + - + name: vims + format: vims + test_family: Features + visualizations: + - + name: duration + fields: + - field: details.vIMS.duration + - field: details.orchestrator.duration + - field: details.sig_test.duration + - + name: tests_failures + fields: + - field: details.sig_test.tests + - field: details.sig_test.failures + - field: details.sig_test.passed + - field: details.sig_test.skipped +promise: + - + name: promise + format: normal + test_family: Features + visualizations: + - + name: duration + fields: + - field: details.duration + - + name: tests_failures + fields: + - field: details.tests + - field: details.failures +doctor: + - + name: doctor-notification + test_family: Features + format: normal + visualizations: + - + name: duration + fields: + - field: details.duration diff --git a/utils/test/dashboard/dashboard/mongo2elastic/__init__.py b/utils/test/dashboard/dashboard/mongo2elastic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/test/dashboard/dashboard/mongo2elastic/format.py b/utils/test/dashboard/dashboard/mongo2elastic/format.py new file mode 100644 index 000000000..ef485bae0 --- /dev/null +++ b/utils/test/dashboard/dashboard/mongo2elastic/format.py @@ -0,0 +1,186 @@ +#! /usr/bin/env python + + +def _convert_value(value): + return value if value != '' else 0 + + +def _convert_duration(duration): + if (isinstance(duration, str) or isinstance(duration, unicode)) and ':' in duration: + hours, minutes, seconds = duration.split(":") + hours = _convert_value(hours) + minutes = _convert_value(minutes) + seconds = _convert_value(seconds) + int_duration = 3600 * int(hours) + 60 * int(minutes) + float(seconds) + else: + int_duration = duration + return int_duration + + +def format_normal(testcase): + """ + Look for these and leave any of those: + details.duration + details.tests + details.failures + + If none are present, then return False + """ + found = False + testcase_details = testcase['details'] + fields = ['duration', 'tests', 'failures'] + if isinstance(testcase_details, dict): + for key, value in testcase_details.items(): + if key in fields: + found = True + if key == 'duration': + testcase_details[key] = _convert_duration(value) + else: + del testcase_details[key] + + if 'tests' in testcase_details and 'failures' in testcase_details: + testcase_tests = float(testcase_details['tests']) + testcase_failures = float(testcase_details['failures']) + if testcase_tests != 0: + testcase_details['success_percentage'] = 100 * (testcase_tests - testcase_failures) / testcase_tests + else: + testcase_details['success_percentage'] = 0 + + + return found + + +def format_rally(testcase): + """ + Structure: + details.[{summary.duration}] + details.[{summary.nb success}] + details.[{summary.nb tests}] + + Find data for these fields + -> details.duration + -> details.tests + -> details.success_percentage + """ + details = testcase['details'] + summary = None + for item in details: + if 'summary' in item: + summary = item['summary'] + + if not summary: + return False + + testcase['details'] = { + 'duration': summary['duration'], + 'tests': summary['nb tests'], + 'success_percentage': summary['nb success'] + } + return True + + +def _get_statistics(orig_data, stat_fields, stat_values=None): + test_results = {} + for stat_data in orig_data: + for field in stat_fields: + stat_value = stat_data[field] + if stat_value in test_results: + test_results[stat_value] += 1 + else: + test_results[stat_value] = 1 + + if stat_values is not None: + for stat_value in stat_values: + if stat_value not in test_results: + test_results[stat_value] = 0 + + return test_results + + +def format_onos(testcase): + """ + Structure: + details.FUNCvirNet.duration + details.FUNCvirNet.status.[{Case result}] + details.FUNCvirNetL3.duration + details.FUNCvirNetL3.status.[{Case result}] + + Find data for these fields + -> details.FUNCvirNet.duration + -> details.FUNCvirNet.tests + -> details.FUNCvirNet.failures + -> details.FUNCvirNetL3.duration + -> details.FUNCvirNetL3.tests + -> details.FUNCvirNetL3.failures + """ + testcase_details = testcase['details'] + + if 'FUNCvirNet' not in testcase_details or 'FUNCvirNetL3' not in testcase_details: + return False + + funcvirnet_details = testcase_details['FUNCvirNet']['status'] + funcvirnet_stats = _get_statistics(funcvirnet_details, ('Case result',), ('PASS', 'FAIL')) + funcvirnet_passed = funcvirnet_stats['PASS'] + funcvirnet_failed = funcvirnet_stats['FAIL'] + funcvirnet_all = funcvirnet_passed + funcvirnet_failed + + funcvirnetl3_details = testcase_details['FUNCvirNetL3']['status'] + funcvirnetl3_stats = _get_statistics(funcvirnetl3_details, ('Case result',), ('PASS', 'FAIL')) + funcvirnetl3_passed = funcvirnetl3_stats['PASS'] + funcvirnetl3_failed = funcvirnetl3_stats['FAIL'] + funcvirnetl3_all = funcvirnetl3_passed + funcvirnetl3_failed + + testcase_details['FUNCvirNet'] = { + 'duration': _convert_duration(testcase_details['FUNCvirNet']['duration']), + 'tests': funcvirnet_all, + 'failures': funcvirnet_failed + } + testcase_details['FUNCvirNetL3'] = { + 'duration': _convert_duration(testcase_details['FUNCvirNetL3']['duration']), + 'tests': funcvirnetl3_all, + 'failures': funcvirnetl3_failed + } + return True + + +def format_vims(testcase): + """ + Structure: + details.sig_test.result.[{result}] + details.sig_test.duration + details.vIMS.duration + details.orchestrator.duration + + Find data for these fields + -> details.sig_test.duration + -> details.sig_test.tests + -> details.sig_test.failures + -> details.sig_test.passed + -> details.sig_test.skipped + -> details.vIMS.duration + -> details.orchestrator.duration + """ + testcase_details = testcase['details'] + test_results = _get_statistics(testcase_details['sig_test']['result'], + ('result',), + ('Passed', 'Skipped', 'Failed')) + passed = test_results['Passed'] + skipped = test_results['Skipped'] + failures = test_results['Failed'] + all_tests = passed + skipped + failures + testcase['details'] = { + 'sig_test': { + 'duration': testcase_details['sig_test']['duration'], + 'tests': all_tests, + 'failures': failures, + 'passed': passed, + 'skipped': skipped + }, + 'vIMS': { + 'duration': testcase_details['vIMS']['duration'] + }, + 'orchestrator': { + 'duration': testcase_details['orchestrator']['duration'] + } + } + return True diff --git a/utils/test/dashboard/dashboard/mongo2elastic/main.py b/utils/test/dashboard/dashboard/mongo2elastic/main.py new file mode 100644 index 000000000..25b5320d7 --- /dev/null +++ b/utils/test/dashboard/dashboard/mongo2elastic/main.py @@ -0,0 +1,243 @@ +#! /usr/bin/env python + +import datetime +import json +import os +import subprocess +import traceback +import urlparse +import uuid + +import argparse + +from common import logger_utils, elastic_access +from conf import testcases +from conf.config import APIConfig +from mongo2elastic import format + +logger = logger_utils.DashboardLogger('mongo2elastic').get + +parser = argparse.ArgumentParser() +parser.add_argument("-c", "--config-file", + dest='config_file', + help="Config file location") +parser.add_argument('-ld', '--latest-days', + default=0, + type=int, + metavar='N', + help='get entries old at most N days from mongodb and' + ' parse those that are not already in elasticsearch.' + ' If not present, will get everything from mongodb, which is the default') + +args = parser.parse_args() +CONF = APIConfig().parse(args.config_file) + + +tmp_docs_file = './mongo-{}.json'.format(uuid.uuid4()) + + +class DocumentPublisher: + + def __init__(self, doc, fmt, exist_docs, creds, to): + self.doc = doc + self.fmt = fmt + self.creds = creds + self.exist_docs = exist_docs + self.to = to + self.is_formatted = True + + def format(self): + try: + if self._verify_document() and self.fmt: + self.is_formatted = vars(format)[self.fmt](self.doc) + else: + self.is_formatted = False + except Exception: + logger.error("Fail in format testcase[%s]\nerror message: %s" % + (self.doc, traceback.format_exc())) + self.is_formatted = False + finally: + return self + + def publish(self): + if self.is_formatted and self.doc not in self.exist_docs: + self._publish() + + def _publish(self): + status, data = elastic_access.publish_json(self.doc, self.creds, self.to) + if status > 300: + logger.error('Publish record[{}] failed, due to [{}]' + .format(self.doc, json.loads(data)['error']['reason'])) + + def _fix_date(self, date_string): + if isinstance(date_string, dict): + return date_string['$date'] + else: + return date_string[:-3].replace(' ', 'T') + 'Z' + + def _verify_document(self): + """ + Mandatory fields: + installer + pod_name + version + case_name + date + project + details + + these fields must be present and must NOT be None + + Optional fields: + description + + these fields will be preserved if the are NOT None + """ + mandatory_fields = ['installer', + 'pod_name', + 'version', + 'case_name', + 'project_name', + 'details'] + mandatory_fields_to_modify = {'start_date': self._fix_date} + fields_to_swap_or_add = {'scenario': 'version'} + if '_id' in self.doc: + mongo_id = self.doc['_id'] + else: + mongo_id = None + optional_fields = ['description'] + for key, value in self.doc.items(): + if key in mandatory_fields: + if value is None: + # empty mandatory field, invalid input + logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing value" + " for mandatory field '{}'".format(mongo_id, key)) + return False + else: + mandatory_fields.remove(key) + elif key in mandatory_fields_to_modify: + if value is None: + # empty mandatory field, invalid input + logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing value" + " for mandatory field '{}'".format(mongo_id, key)) + return False + else: + self.doc[key] = mandatory_fields_to_modify[key](value) + del mandatory_fields_to_modify[key] + elif key in fields_to_swap_or_add: + if value is None: + swapped_key = fields_to_swap_or_add[key] + swapped_value = self.doc[swapped_key] + logger.info("Swapping field '{}' with value None for '{}' with value '{}'.".format(key, swapped_key, + swapped_value)) + self.doc[key] = swapped_value + del fields_to_swap_or_add[key] + else: + del fields_to_swap_or_add[key] + elif key in optional_fields: + if value is None: + # empty optional field, remove + del self.doc[key] + optional_fields.remove(key) + else: + # unknown field + del self.doc[key] + + if len(mandatory_fields) > 0: + # some mandatory fields are missing + logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing" + " mandatory field(s) '{}'".format(mongo_id, mandatory_fields)) + return False + elif len(mandatory_fields_to_modify) > 0: + # some mandatory fields are missing + logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing" + " mandatory field(s) '{}'".format(mongo_id, mandatory_fields_to_modify.keys())) + return False + else: + if len(fields_to_swap_or_add) > 0: + for key, swap_key in fields_to_swap_or_add.iteritems(): + self.doc[key] = self.doc[swap_key] + + return True + + +class DocumentsPublisher: + + def __init__(self, project, case, fmt, days, elastic_url, creds, to): + self.project = project + self.case = case + self.fmt = fmt + self.days = days + self.elastic_url = elastic_url + self.creds = creds + self.to = to + self.existed_docs = [] + + def export(self): + if self.days > 0: + past_time = datetime.datetime.today() - datetime.timedelta(days=self.days) + query = '''{{ + "project_name": "{}", + "case_name": "{}", + "start_date": {{"$gt" : "{}"}} + }}'''.format(self.project, self.case, past_time) + else: + query = '''{{ + "project_name": "{}", + "case_name": "{}" + }}'''.format(self.project, self.case) + cmd = ['mongoexport', + '--db', 'test_results_collection', + '--collection', 'results', + '--query', '{}'.format(query), + '--out', '{}'.format(tmp_docs_file)] + try: + subprocess.check_call(cmd) + return self + except Exception, err: + logger.error("export mongodb failed: %s" % err) + self._remove() + exit(-1) + + def get_existed_docs(self): + self.existed_docs = elastic_access.get_elastic_docs_by_days(self.elastic_url, self.creds, self.days) + return self + + def publish(self): + try: + with open(tmp_docs_file) as fdocs: + for doc_line in fdocs: + DocumentPublisher(json.loads(doc_line), + self.fmt, + self.existed_docs, + self.creds, + self.to).format().publish() + finally: + fdocs.close() + self._remove() + + def _remove(self): + if os.path.exists(tmp_docs_file): + os.remove(tmp_docs_file) + + +def main(): + base_elastic_url = urlparse.urljoin(CONF.elastic_url, '/test_results/mongo2elastic') + to = CONF.destination + days = args.latest_days + es_creds = CONF.elastic_creds + + if to == 'elasticsearch': + to = base_elastic_url + + for project, case_dicts in testcases.testcases_yaml.items(): + for case_dict in case_dicts: + case = case_dict.get('name') + fmt = testcases.compose_format(case_dict.get('format')) + DocumentsPublisher(project, + case, + fmt, + days, + base_elastic_url, + es_creds, + to).export().get_existed_docs().publish() diff --git a/utils/test/dashboard/dashboard/mongo2elastic_main.py b/utils/test/dashboard/dashboard/mongo2elastic_main.py new file mode 100644 index 000000000..141d8f3ab --- /dev/null +++ b/utils/test/dashboard/dashboard/mongo2elastic_main.py @@ -0,0 +1,4 @@ +from mongo2elastic.main import main + +if __name__ == '__main__': + main() diff --git a/utils/test/dashboard/etc/config.ini b/utils/test/dashboard/etc/config.ini new file mode 100644 index 000000000..b94ac7b4f --- /dev/null +++ b/utils/test/dashboard/etc/config.ini @@ -0,0 +1,14 @@ +# to add a new parameter in the config file, +# the CONF object in config.ini must be updated +[elastic] +url = http://localhost:9200 +creds = + +[output] +# elasticsearch or console +destination = elasticsearch + +[kibana] +url = http://10.63.243.17:5601/app/kibana +js = true +js_path = /usr/share/nginx/html/kibana_dashboards/conf.js diff --git a/utils/test/dashboard/kibana_cleanup.py b/utils/test/dashboard/kibana_cleanup.py new file mode 100644 index 000000000..9ce4994f5 --- /dev/null +++ b/utils/test/dashboard/kibana_cleanup.py @@ -0,0 +1,41 @@ +#! /usr/bin/env python +import logging +import urlparse + +import argparse + +from dashboard.common import elastic_access + +logger = logging.getLogger('clear_kibana') +logger.setLevel(logging.DEBUG) +file_handler = logging.FileHandler('/var/log/{}.log'.format('clear_kibana')) +file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) +logger.addHandler(file_handler) + + +def delete_all(url, es_creds): + ids = elastic_access.get_elastic_docs(url, es_creds, body=None, field='_id') + for id in ids: + del_url = '/'.join([url, id]) + elastic_access.delete_request(del_url, es_creds) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Delete saved kibana searches, visualizations and dashboards') + parser.add_argument('-e', '--elasticsearch-url', default='http://localhost:9200', + help='the url of elasticsearch, defaults to http://localhost:9200') + + parser.add_argument('-u', '--elasticsearch-username', default=None, + help='The username with password for elasticsearch in format username:password') + + args = parser.parse_args() + base_elastic_url = args.elasticsearch_url + es_creds = args.elasticsearch_username + + urls = (urlparse.urljoin(base_elastic_url, '/.kibana/visualization'), + urlparse.urljoin(base_elastic_url, '/.kibana/dashboard'), + urlparse.urljoin(base_elastic_url, '/.kibana/search')) + + for url in urls: + delete_all(url, es_creds) + diff --git a/utils/test/scripts/backup-db.sh b/utils/test/scripts/backup-db.sh deleted file mode 100644 index 35c3fbe5a..000000000 --- a/utils/test/scripts/backup-db.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# SPDX-license-identifier: Apache-2.0 -############################################################################## -# Copyright (c) 2016 Orange and others. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## -echo "Backup Test collection DB" -now=$(date +"%m_%d_%Y_%H_%M_%S") -echo $now -echo " ------------- " -TARGET_DIR=./$now -TEST_RESULT_DB_BACKUP="test_collection_db."$now".tar.gz" - -echo "Create Directory for backup" -mkdir -p $TARGET_DIR - -echo "Export results" -mongoexport --db test_results_collection -c results --out $TARGET_DIR/backup-results.json -echo "Export test cases" -mongoexport --db test_results_collection -c testcases --out $TARGET_DIR/backup-cases.json -echo "Export projects" -mongoexport --db test_results_collection -c projects --out $TARGET_DIR/backup-projects.json -echo "Export pods" -mongoexport --db test_results_collection -c pods --out $TARGET_DIR/backup-pod.json - -echo "Create tar.gz" -#tar -cvzf $TEST_RESULT_DB_BACKUP $TARGET_DIR - -echo "Delete temp directory" -#rm -Rf $TARGET_DIR diff --git a/utils/test/scripts/config.ini b/utils/test/scripts/config.ini deleted file mode 100644 index 63d283dc8..000000000 --- a/utils/test/scripts/config.ini +++ /dev/null @@ -1,14 +0,0 @@ -# to add a new parameter in the config file, -# the CONF object in config.ini must be updated -[elastic] -url = http://localhost:9200 -creds = - -[output] -# elasticsearch or console -destination = elasticsearch - -[kibana] -url = http://10.63.243.17/kibana/app/kibana -js = true -js_path = /usr/share/nginx/html/kibana_dashboards/conf.js diff --git a/utils/test/scripts/config.py b/utils/test/scripts/config.py deleted file mode 100644 index 2d447a7ba..000000000 --- a/utils/test/scripts/config.py +++ /dev/null @@ -1,88 +0,0 @@ -#! /usr/bin/env python - -from ConfigParser import SafeConfigParser, NoOptionError - - -class ParseError(Exception): - """ - Custom exception class for config file - """ - - def __init__(self, message): - self.msg = message - - def __str__(self): - return 'error parsing config file : %s' % self.msg - - -class APIConfig: - """ - The purpose of this class is to load values correctly from the config file. - Each key is declared as an attribute in __init__() and linked in parse() - """ - - def __init__(self): - self._default_config_location = "./config.ini" - self.elastic_url = 'http://localhost:9200' - self.elastic_creds = None - self.destination = 'elasticsearch' - self.kibana_url = None - self.is_js = True - self.js_path = None - - def _get_str_parameter(self, section, param): - try: - return self._parser.get(section, param) - except NoOptionError: - raise ParseError("[%s.%s] parameter not found" % (section, param)) - - def _get_int_parameter(self, section, param): - try: - return int(self._get_str_parameter(section, param)) - except ValueError: - raise ParseError("[%s.%s] not an int" % (section, param)) - - def _get_bool_parameter(self, section, param): - result = self._get_str_parameter(section, param) - if str(result).lower() == 'true': - return True - if str(result).lower() == 'false': - return False - - raise ParseError( - "[%s.%s : %s] not a boolean" % (section, param, result)) - - @staticmethod - def parse(config_location=None): - obj = APIConfig() - - if config_location is None: - config_location = obj._default_config_location - - obj._parser = SafeConfigParser() - obj._parser.read(config_location) - if not obj._parser: - raise ParseError("%s not found" % config_location) - - # Linking attributes to keys from file with their sections - obj.elastic_url = obj._get_str_parameter("elastic", "url") - obj.elastic_creds = obj._get_str_parameter("elastic", "creds") - obj.destination = obj._get_str_parameter("output", "destination") - obj.kibana_url = obj._get_str_parameter("kibana", "url") - obj.is_js = obj._get_bool_parameter("kibana", "js") - obj.js_path = obj._get_str_parameter("kibana", "js_path") - - return obj - - def __str__(self): - return "elastic_url = %s \n" \ - "elastic_creds = %s \n" \ - "destination = %s \n" \ - "kibana_url = %s \n" \ - "is_js = %s \n" \ - "js_path = %s \n" % (self.elastic_url, - self.elastic_creds, - self.destination, - self.kibana_url, - self.is_js, - self.js_path) diff --git a/utils/test/scripts/create_kibana_dashboards.py b/utils/test/scripts/create_kibana_dashboards.py deleted file mode 100644 index 19d5b5e52..000000000 --- a/utils/test/scripts/create_kibana_dashboards.py +++ /dev/null @@ -1,375 +0,0 @@ -#! /usr/bin/env python -import json -import urlparse - -import argparse - -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") - -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, 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.scenarios = scenarios - self.visualization = visualization - self._visualization_title = None - self._kibana_visualizations = [] - self._kibana_dashboard = None - self._create_visualizations() - self._create() - - def _create_visualizations(self): - for scenario in self.scenarios: - self._kibana_visualizations.append(KibanaVisualization(self.project_name, - self.case_name, - self.installer, - self.pod, - scenario, - self.visualization)) - - self._visualization_title = self._kibana_visualizations[0].vis_state_title - - def _publish_visualizations(self): - 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_creds, url) - - def _construct_panels(self): - size_x = 6 - size_y = 3 - max_columns = 7 - column = 1 - row = 1 - panel_index = 1 - panels_json = [] - for visualization in self._kibana_visualizations: - panels_json.append({ - "id": visualization.id, - "type": 'visualization', - "panelIndex": panel_index, - "size_x": size_x, - "size_y": size_y, - "col": column, - "row": row - }) - panel_index += 1 - column += size_x - if column > max_columns: - column = 1 - row += size_y - return json.dumps(panels_json, separators=(',', ':')) - - def _create(self): - self['title'] = '{} {} {} {} {}'.format(self.project_name, - self.case_name, - self.installer, - self._visualization_title, - self.pod) - self.id = self['title'].replace(' ', '-').replace('/', '-') - - self['hits'] = 0 - self['description'] = "Kibana dashboard for project_name '{}', case_name '{}', installer '{}', data '{}' and" \ - " pod '{}'".format(self.project_name, - self.case_name, - self.installer, - self._visualization_title, - self.pod) - self['panelsJSON'] = self._construct_panels() - self['optionsJSON'] = json.dumps({ - "darkTheme": False - }, - separators=(',', ':')) - self['uiStateJSON'] = "{}" - self['scenario'] = 1 - self['timeRestore'] = False - self['kibanaSavedObjectMeta'] = { - 'searchSourceJSON': json.dumps({ - "filter": [ - { - "query": { - "query_string": { - "query": "*", - "analyze_wildcard": True - } - } - } - ] - }, - separators=(',', ':')) - } - - 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_creds, url) - - def publish(self): - self._publish_visualizations() - self._publish() - - -class KibanaSearchSourceJSON(dict): - """ - "filter": [ - {"match": {"installer": {"query": installer, "type": "phrase"}}}, - {"match": {"project_name": {"query": project_name, "type": "phrase"}}}, - {"match": {"case_name": {"query": case_name, "type": "phrase"}}} - ] - """ - - 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": {"scenario": {"query": scenario, "type": "phrase"}}} - ] - if pod != 'all': - self["filter"].append({"match": {"pod_name": {"query": pod, "type": "phrase"}}}) - - -class VisualizationState(dict): - def __init__(self, visualization): - super(VisualizationState, self).__init__() - name = visualization.get('name') - fields = visualization.get('fields') - - if name == 'tests_failures': - mode = 'grouped' - metric_type = 'sum' - self['type'] = 'histogram' - else: - # duration or success_percentage - mode = 'stacked' - metric_type = 'avg' - self['type'] = 'line' - - self['params'] = { - "shareYAxis": True, - "addTooltip": True, - "addLegend": True, - "smoothLines": False, - "scale": "linear", - "interpolate": "linear", - "mode": mode, - "times": [], - "addTimeMarker": False, - "defaultYExtents": False, - "setYExtents": False, - "yAxis": {} - } - - self['aggs'] = [] - - i = 1 - for field in fields: - self['aggs'].append({ - "id": str(i), - "type": metric_type, - "schema": "metric", - "params": { - "field": field.get('field') - } - }) - i += 1 - - self['aggs'].append({ - "id": str(i), - "type": 'date_histogram', - "schema": "segment", - "params": { - "field": "start_date", - "interval": "auto", - "customInterval": "2h", - "min_doc_count": 1, - "extended_bounds": {} - } - }) - - self['listeners'] = {} - self['title'] = ' '.join(['{} {}'.format(x['type'], x['params']['field']) for x in self['aggs'] - if x['schema'] == 'metric']) - - -class KibanaVisualization(dict): - def __init__(self, project_name, case_name, installer, pod, scenario, visualization): - """ - We need two things - 1. filter created from - project_name - case_name - installer - pod - scenario - 2. visualization state - field for y axis (metric) with type (avg, sum, etc.) - field for x axis (segment) with type (date_histogram) - - :return: - """ - super(KibanaVisualization, self).__init__() - vis_state = VisualizationState(visualization) - self.vis_state_title = vis_state['title'] - self['title'] = '{} {} {} {} {} {}'.format(project_name, - case_name, - self.vis_state_title, - installer, - pod, - 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 scenario '{}'".format(project_name, - case_name, - self.vis_state_title, - installer, - pod, - scenario) - self['scenario'] = 1 - self['kibanaSavedObjectMeta'] = {"searchSourceJSON": json.dumps(KibanaSearchSourceJSON(project_name, - case_name, - installer, - pod, - scenario), - separators=(',', ':'))} - - -def _get_pods_and_scenarios(project_name, case_name, installer): - query_json = json.JSONEncoder().encode({ - "query": { - "bool": { - "must": [ - {"match_all": {}} - ], - "filter": [ - {"match": {"installer": {"query": installer, "type": "phrase"}}}, - {"match": {"project_name": {"query": project_name, "type": "phrase"}}}, - {"match": {"case_name": {"query": case_name, "type": "phrase"}}} - ] - } - } - }) - - elastic_data = shared_utils.get_elastic_docs(urlparse.urljoin(base_elastic_url, '/test_results/mongo2elastic'), - es_creds, query_json) - - pods_and_scenarios = {} - - for data in elastic_data: - pod = data['pod_name'] - if pod in pods_and_scenarios: - pods_and_scenarios[pod].add(data['scenario']) - else: - pods_and_scenarios[pod] = {data['scenario']} - - if 'all' in pods_and_scenarios: - pods_and_scenarios['all'].add(data['scenario']) - else: - pods_and_scenarios['all'] = {data['scenario']} - - return pods_and_scenarios - - -def construct_dashboards(): - """ - iterate over testcase and installer - 1. get available pods for each testcase/installer pair - 2. get available scenario for each testcase/installer/pod tuple - 3. construct KibanaInput and append - - :return: list of KibanaDashboards - """ - kibana_dashboards = [] - 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 - - -def generate_js_inputs(js_file_path, kibana_url, dashboards): - js_dict = {} - for dashboard in dashboards: - dashboard_meta = dashboard['metadata'] - test_family = dashboard_meta['test_family'] - test_label = dashboard_meta['label'] - - if test_family not in js_dict: - js_dict[test_family] = {} - - js_test_family = js_dict[test_family] - - if test_label not in js_test_family: - js_test_family[test_label] = {} - - js_test_label = js_test_family[test_label] - - if dashboard.installer not in js_test_label: - js_test_label[dashboard.installer] = {} - - js_installer = js_test_label[dashboard.installer] - js_installer[dashboard.pod] = kibana_url + '#/dashboard/' + dashboard.id - - with open(js_file_path, 'w+') as js_file_fdesc: - js_file_fdesc.write('var kibana_dashboard_links = ') - js_file_fdesc.write(str(js_dict).replace("u'", "'")) - - -if __name__ == '__main__': - 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() - - for kibana_dashboard in dashboards: - kibana_dashboard.publish() - - if generate_inputs: - generate_js_inputs(input_file_path, kibana_url, dashboards) diff --git a/utils/test/scripts/kibana_cleanup.py b/utils/test/scripts/kibana_cleanup.py deleted file mode 100644 index d87d9a285..000000000 --- a/utils/test/scripts/kibana_cleanup.py +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env python -import logging -import urlparse - -import argparse - -import shared_utils - -logger = logging.getLogger('clear_kibana') -logger.setLevel(logging.DEBUG) -file_handler = logging.FileHandler('/var/log/{}.log'.format('clear_kibana')) -file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) -logger.addHandler(file_handler) - - -def delete_all(url, es_creds): - ids = shared_utils.get_elastic_docs(url, es_creds, body=None, field='_id') - for id in ids: - del_url = '/'.join([url, id]) - shared_utils.delete_request(del_url, es_creds) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Delete saved kibana searches, visualizations and dashboards') - parser.add_argument('-e', '--elasticsearch-url', default='http://localhost:9200', - help='the url of elasticsearch, defaults to http://localhost:9200') - - parser.add_argument('-u', '--elasticsearch-username', default=None, - help='The username with password for elasticsearch in format username:password') - - args = parser.parse_args() - base_elastic_url = args.elasticsearch_url - es_creds = args.elasticsearch_username - - urls = (urlparse.urljoin(base_elastic_url, '/.kibana/visualization'), - urlparse.urljoin(base_elastic_url, '/.kibana/dashboard'), - urlparse.urljoin(base_elastic_url, '/.kibana/search')) - - for url in urls: - delete_all(url, es_creds) - diff --git a/utils/test/scripts/logger_utils.py b/utils/test/scripts/logger_utils.py deleted file mode 100644 index 25d28a582..000000000 --- a/utils/test/scripts/logger_utils.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python -# -# feng.xiaowei@zte.com.cn -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Logging levels: -# Level Numeric value -# CRITICAL 50 -# ERROR 40 -# WARNING 30 -# INFO 20 -# DEBUG 10 -# NOTSET 0 -# -# Usage: -# import functest_logger as fl -# logger = fl.Logger("script_name").getLogger() -# logger.info("message to be shown with - INFO - ") -# logger.debug("message to be shown with - DEBUG -") - -import logging -import os - - -class Logger(object): - file_path = '/var/log' - formatter = logging.Formatter('%(asctime)s - %(name)s - ' - '%(levelname)s - %(message)s') - - def __init__(self, logger_name): - - IF_DEBUG = os.getenv('IF_DEBUG') - - self.logger_name = logger_name - self.logger = logging.getLogger(logger_name) - self.logger.propagate = 0 - self.logger.setLevel(logging.DEBUG) - - ch = logging.StreamHandler() - ch.setFormatter(self.formatter) - if IF_DEBUG is not None and IF_DEBUG.lower() == "true": - ch.setLevel(logging.DEBUG) - else: - ch.setLevel(logging.INFO) - self.logger.addHandler(ch) - - hdlr = logging.FileHandler('%s/%s.log' % (self.file_path, logger_name)) - hdlr.setFormatter(self.formatter) - hdlr.setLevel(logging.DEBUG) - self.logger.addHandler(hdlr) - - @property - def get(self): - return self.logger - - -class KibanaDashboardLogger(Logger): - file_path = '/var/log/kibana_dashboard' - - def __init__(self, logger_name): - super(KibanaDashboardLogger, self).__init__(logger_name) - diff --git a/utils/test/scripts/mongo2elastic_format.py b/utils/test/scripts/mongo2elastic_format.py deleted file mode 100644 index ef485bae0..000000000 --- a/utils/test/scripts/mongo2elastic_format.py +++ /dev/null @@ -1,186 +0,0 @@ -#! /usr/bin/env python - - -def _convert_value(value): - return value if value != '' else 0 - - -def _convert_duration(duration): - if (isinstance(duration, str) or isinstance(duration, unicode)) and ':' in duration: - hours, minutes, seconds = duration.split(":") - hours = _convert_value(hours) - minutes = _convert_value(minutes) - seconds = _convert_value(seconds) - int_duration = 3600 * int(hours) + 60 * int(minutes) + float(seconds) - else: - int_duration = duration - return int_duration - - -def format_normal(testcase): - """ - Look for these and leave any of those: - details.duration - details.tests - details.failures - - If none are present, then return False - """ - found = False - testcase_details = testcase['details'] - fields = ['duration', 'tests', 'failures'] - if isinstance(testcase_details, dict): - for key, value in testcase_details.items(): - if key in fields: - found = True - if key == 'duration': - testcase_details[key] = _convert_duration(value) - else: - del testcase_details[key] - - if 'tests' in testcase_details and 'failures' in testcase_details: - testcase_tests = float(testcase_details['tests']) - testcase_failures = float(testcase_details['failures']) - if testcase_tests != 0: - testcase_details['success_percentage'] = 100 * (testcase_tests - testcase_failures) / testcase_tests - else: - testcase_details['success_percentage'] = 0 - - - return found - - -def format_rally(testcase): - """ - Structure: - details.[{summary.duration}] - details.[{summary.nb success}] - details.[{summary.nb tests}] - - Find data for these fields - -> details.duration - -> details.tests - -> details.success_percentage - """ - details = testcase['details'] - summary = None - for item in details: - if 'summary' in item: - summary = item['summary'] - - if not summary: - return False - - testcase['details'] = { - 'duration': summary['duration'], - 'tests': summary['nb tests'], - 'success_percentage': summary['nb success'] - } - return True - - -def _get_statistics(orig_data, stat_fields, stat_values=None): - test_results = {} - for stat_data in orig_data: - for field in stat_fields: - stat_value = stat_data[field] - if stat_value in test_results: - test_results[stat_value] += 1 - else: - test_results[stat_value] = 1 - - if stat_values is not None: - for stat_value in stat_values: - if stat_value not in test_results: - test_results[stat_value] = 0 - - return test_results - - -def format_onos(testcase): - """ - Structure: - details.FUNCvirNet.duration - details.FUNCvirNet.status.[{Case result}] - details.FUNCvirNetL3.duration - details.FUNCvirNetL3.status.[{Case result}] - - Find data for these fields - -> details.FUNCvirNet.duration - -> details.FUNCvirNet.tests - -> details.FUNCvirNet.failures - -> details.FUNCvirNetL3.duration - -> details.FUNCvirNetL3.tests - -> details.FUNCvirNetL3.failures - """ - testcase_details = testcase['details'] - - if 'FUNCvirNet' not in testcase_details or 'FUNCvirNetL3' not in testcase_details: - return False - - funcvirnet_details = testcase_details['FUNCvirNet']['status'] - funcvirnet_stats = _get_statistics(funcvirnet_details, ('Case result',), ('PASS', 'FAIL')) - funcvirnet_passed = funcvirnet_stats['PASS'] - funcvirnet_failed = funcvirnet_stats['FAIL'] - funcvirnet_all = funcvirnet_passed + funcvirnet_failed - - funcvirnetl3_details = testcase_details['FUNCvirNetL3']['status'] - funcvirnetl3_stats = _get_statistics(funcvirnetl3_details, ('Case result',), ('PASS', 'FAIL')) - funcvirnetl3_passed = funcvirnetl3_stats['PASS'] - funcvirnetl3_failed = funcvirnetl3_stats['FAIL'] - funcvirnetl3_all = funcvirnetl3_passed + funcvirnetl3_failed - - testcase_details['FUNCvirNet'] = { - 'duration': _convert_duration(testcase_details['FUNCvirNet']['duration']), - 'tests': funcvirnet_all, - 'failures': funcvirnet_failed - } - testcase_details['FUNCvirNetL3'] = { - 'duration': _convert_duration(testcase_details['FUNCvirNetL3']['duration']), - 'tests': funcvirnetl3_all, - 'failures': funcvirnetl3_failed - } - return True - - -def format_vims(testcase): - """ - Structure: - details.sig_test.result.[{result}] - details.sig_test.duration - details.vIMS.duration - details.orchestrator.duration - - Find data for these fields - -> details.sig_test.duration - -> details.sig_test.tests - -> details.sig_test.failures - -> details.sig_test.passed - -> details.sig_test.skipped - -> details.vIMS.duration - -> details.orchestrator.duration - """ - testcase_details = testcase['details'] - test_results = _get_statistics(testcase_details['sig_test']['result'], - ('result',), - ('Passed', 'Skipped', 'Failed')) - passed = test_results['Passed'] - skipped = test_results['Skipped'] - failures = test_results['Failed'] - all_tests = passed + skipped + failures - testcase['details'] = { - 'sig_test': { - 'duration': testcase_details['sig_test']['duration'], - 'tests': all_tests, - 'failures': failures, - 'passed': passed, - 'skipped': skipped - }, - 'vIMS': { - 'duration': testcase_details['vIMS']['duration'] - }, - 'orchestrator': { - 'duration': testcase_details['orchestrator']['duration'] - } - } - return True diff --git a/utils/test/scripts/mongo_to_elasticsearch.py b/utils/test/scripts/mongo_to_elasticsearch.py deleted file mode 100644 index 777eda6ad..000000000 --- a/utils/test/scripts/mongo_to_elasticsearch.py +++ /dev/null @@ -1,248 +0,0 @@ -#! /usr/bin/env python - -import datetime -import json -import os -import subprocess -import traceback -import urlparse -import uuid - -import argparse - -import logger_utils -import mongo2elastic_format -import shared_utils -import testcases_parser -from config import APIConfig - -logger = logger_utils.KibanaDashboardLogger('mongo2elastic').get - -parser = argparse.ArgumentParser() -parser.add_argument("-c", "--config-file", - dest='config_file', - help="Config file location") -parser.add_argument('-ld', '--latest-days', - default=0, - type=int, - metavar='N', - help='get entries old at most N days from mongodb and' - ' parse those that are not already in elasticsearch.' - ' If not present, will get everything from mongodb, which is the default') - -args = parser.parse_args() -CONF = APIConfig().parse(args.config_file) - - -tmp_docs_file = './mongo-{}.json'.format(uuid.uuid4()) - - -class DocumentPublisher: - - def __init__(self, doc, fmt, exist_docs, creds, to): - self.doc = doc - self.fmt = fmt - self.creds = creds - self.exist_docs = exist_docs - self.to = to - self.is_formatted = True - - def format(self): - try: - if self._verify_document() and self.fmt: - self.is_formatted = vars(mongo2elastic_format)[self.fmt](self.doc) - else: - self.is_formatted = False - except Exception: - logger.error("Fail in format testcase[%s]\nerror message: %s" % - (self.doc, traceback.format_exc())) - self.is_formatted = False - finally: - return self - - def publish(self): - if self.is_formatted and self.doc not in self.exist_docs: - self._publish() - - def _publish(self): - status, data = shared_utils.publish_json(self.doc, self.creds, self.to) - if status > 300: - logger.error('Publish record[{}] failed, due to [{}]' - .format(self.doc, json.loads(data)['error']['reason'])) - - def _fix_date(self, date_string): - if isinstance(date_string, dict): - return date_string['$date'] - else: - return date_string[:-3].replace(' ', 'T') + 'Z' - - def _verify_document(self): - """ - Mandatory fields: - installer - pod_name - version - case_name - date - project - details - - these fields must be present and must NOT be None - - Optional fields: - description - - these fields will be preserved if the are NOT None - """ - mandatory_fields = ['installer', - 'pod_name', - 'version', - 'case_name', - 'project_name', - 'details'] - mandatory_fields_to_modify = {'start_date': self._fix_date} - fields_to_swap_or_add = {'scenario': 'version'} - if '_id' in self.doc: - mongo_id = self.doc['_id'] - else: - mongo_id = None - optional_fields = ['description'] - for key, value in self.doc.items(): - if key in mandatory_fields: - if value is None: - # empty mandatory field, invalid input - logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing value" - " for mandatory field '{}'".format(mongo_id, key)) - return False - else: - mandatory_fields.remove(key) - elif key in mandatory_fields_to_modify: - if value is None: - # empty mandatory field, invalid input - logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing value" - " for mandatory field '{}'".format(mongo_id, key)) - return False - else: - self.doc[key] = mandatory_fields_to_modify[key](value) - del mandatory_fields_to_modify[key] - elif key in fields_to_swap_or_add: - if value is None: - swapped_key = fields_to_swap_or_add[key] - swapped_value = self.doc[swapped_key] - logger.info("Swapping field '{}' with value None for '{}' with value '{}'.".format(key, swapped_key, - swapped_value)) - self.doc[key] = swapped_value - del fields_to_swap_or_add[key] - else: - del fields_to_swap_or_add[key] - elif key in optional_fields: - if value is None: - # empty optional field, remove - del self.doc[key] - optional_fields.remove(key) - else: - # unknown field - del self.doc[key] - - if len(mandatory_fields) > 0: - # some mandatory fields are missing - logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing" - " mandatory field(s) '{}'".format(mongo_id, mandatory_fields)) - return False - elif len(mandatory_fields_to_modify) > 0: - # some mandatory fields are missing - logger.info("Skipping testcase with mongo _id '{}' because the testcase was missing" - " mandatory field(s) '{}'".format(mongo_id, mandatory_fields_to_modify.keys())) - return False - else: - if len(fields_to_swap_or_add) > 0: - for key, swap_key in fields_to_swap_or_add.iteritems(): - self.doc[key] = self.doc[swap_key] - - return True - - -class DocumentsPublisher: - - def __init__(self, project, case, fmt, days, elastic_url, creds, to): - self.project = project - self.case = case - self.fmt = fmt - self.days = days - self.elastic_url = elastic_url - self.creds = creds - self.to = to - self.existed_docs = [] - - def export(self): - if self.days > 0: - past_time = datetime.datetime.today() - datetime.timedelta(days=self.days) - query = '''{{ - "project_name": "{}", - "case_name": "{}", - "start_date": {{"$gt" : "{}"}} - }}'''.format(self.project, self.case, past_time) - else: - query = '''{{ - "project_name": "{}", - "case_name": "{}" - }}'''.format(self.project, self.case) - cmd = ['mongoexport', - '--db', 'test_results_collection', - '--collection', 'results', - '--query', '{}'.format(query), - '--out', '{}'.format(tmp_docs_file)] - try: - subprocess.check_call(cmd) - return self - except Exception, err: - logger.error("export mongodb failed: %s" % err) - self._remove() - exit(-1) - - def get_existed_docs(self): - self.existed_docs = shared_utils.get_elastic_docs_by_days(self.elastic_url, self.creds, self.days) - return self - - def publish(self): - try: - with open(tmp_docs_file) as fdocs: - for doc_line in fdocs: - DocumentPublisher(json.loads(doc_line), - self.fmt, - self.existed_docs, - self.creds, - self.to).format().publish() - finally: - fdocs.close() - self._remove() - - def _remove(self): - if os.path.exists(tmp_docs_file): - os.remove(tmp_docs_file) - - -def main(): - base_elastic_url = urlparse.urljoin(CONF.elastic_url, '/test_results/mongo2elastic') - to = CONF.destination - days = args.latest_days - es_creds = CONF.elastic_creds - - if to == 'elasticsearch': - to = base_elastic_url - - for project, case_dicts in testcases_parser.testcases_yaml.items(): - for case_dict in case_dicts: - case = case_dict.get('name') - fmt = testcases_parser.compose_format(case_dict.get('format')) - DocumentsPublisher(project, - case, - fmt, - days, - base_elastic_url, - es_creds, - to).export().get_existed_docs().publish() - - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/utils/test/scripts/shared_utils.py b/utils/test/scripts/shared_utils.py deleted file mode 100644 index e90a17fa3..000000000 --- a/utils/test/scripts/shared_utils.py +++ /dev/null @@ -1,64 +0,0 @@ -import json - -import urllib3 - -http = urllib3.PoolManager() - - -def delete_request(url, creds, body=None): - headers = urllib3.make_headers(basic_auth=creds) - http.request('DELETE', url, headers=headers, body=body) - - -def publish_json(json_ojb, creds, to): - json_dump = json.dumps(json_ojb) - if to == 'stdout': - print json_dump - return 200, None - else: - headers = urllib3.make_headers(basic_auth=creds) - result = http.request('POST', to, headers=headers, body=json_dump) - return result.status, result.data - - -def _get_nr_of_hits(elastic_json): - return elastic_json['hits']['total'] - - -def get_elastic_docs(elastic_url, creds, body=None, field = '_source'): - - # 1. get the number of results - headers = urllib3.make_headers(basic_auth=creds) - elastic_json = json.loads(http.request('GET', elastic_url + '/_search?size=0', headers=headers, body=body).data) - print elastic_json - nr_of_hits = _get_nr_of_hits(elastic_json) - - # 2. get all results - elastic_json = json.loads(http.request('GET', elastic_url + '/_search?size={}'.format(nr_of_hits), headers=headers, body=body).data) - - elastic_docs = [] - for hit in elastic_json['hits']['hits']: - elastic_docs.append(hit[field]) - return elastic_docs - - -def get_elastic_docs_by_days(elastic_url, creds, days): - if days == 0: - body = '''{ - "query": { - "match_all": {} - } - }''' - elif days > 0: - body = '''{{ - "query" : {{ - "range" : {{ - "start_date" : {{ - "gte" : "now-{}d" - }} - }} - }} - }}'''.format(days) - else: - raise Exception('Update days must be non-negative') - return get_elastic_docs(elastic_url, creds, body) diff --git a/utils/test/scripts/testcases.yaml b/utils/test/scripts/testcases.yaml deleted file mode 100644 index 9c33d2e6b..000000000 --- a/utils/test/scripts/testcases.yaml +++ /dev/null @@ -1,138 +0,0 @@ -functest: - - - name: tempest_smoke_serial - format: normal - test_family: VIM - visualizations: - - - name: duration - fields: - - field: details.duration - - - name: tests_failures - fields: - - field: details.tests - - field: details.failures - - - name: success_percentage - fields: - - field: details.success_percentage - - - name: rally_sanity - test_family: VIM - format: rally - visualizations: - - - name: duration - fields: - - field: details.duration - - - name: tests_failures - fields: - - field: details.tests - - - name: success_percentage - fields: - - field: details.success_percentage - - - name: vping_ssh - format: normal - test_family: VIM - visualizations: - - - name: duration - fields: - - field: details.duration - - - name: vping_userdata - format: normal - test_family: VIM - visualizations: - - - name: duration - fields: - - field: details.duration - - - name: odl - test_family: Controller - format: odl - visualizations: - - - name: tests_failures - fields: - - field: details.tests - - field: details.failures - - - name: success_percentage - fields: - - field: details.success_percentage - - - name: onos - format: onos - test_family: Controller - visualizations: - - - name: duration - label: FUNCvirNet - fields: - - field: details.FUNCvirNet.duration - - - name: duration - label: FUNCvirNetL3 - fields: - - field: details.FUNCvirNetL3.duration - - - name: tests_failures - label: FUNCvirNet - fields: - - field: details.FUNCvirNet.tests - - field: details.FUNCvirNet.failures - - - name: tests_failures - label: FUNCvirNetL3 - fields: - - field: details.FUNCvirNetL3.tests - - field: details.FUNCvirNetL3.failures - - - name: vims - format: vims - test_family: Features - visualizations: - - - name: duration - fields: - - field: details.vIMS.duration - - field: details.orchestrator.duration - - field: details.sig_test.duration - - - name: tests_failures - fields: - - field: details.sig_test.tests - - field: details.sig_test.failures - - field: details.sig_test.passed - - field: details.sig_test.skipped -promise: - - - name: promise - format: normal - test_family: Features - visualizations: - - - name: duration - fields: - - field: details.duration - - - name: tests_failures - fields: - - field: details.tests - - field: details.failures -doctor: - - - name: doctor-notification - test_family: Features - format: normal - visualizations: - - - name: duration - fields: - - field: details.duration diff --git a/utils/test/scripts/testcases_parser.py b/utils/test/scripts/testcases_parser.py deleted file mode 100644 index cf9599858..000000000 --- a/utils/test/scripts/testcases_parser.py +++ /dev/null @@ -1,24 +0,0 @@ -import yaml - - -with open('./testcases.yaml') as f: - testcases_yaml = yaml.safe_load(f) -f.close() - - -def compose_format(fmt): - return 'format_' + fmt.strip() - - -def get_format(project, case): - testcases = testcases_yaml.get(project) - if isinstance(testcases, list): - for case_dict in testcases: - if case_dict['name'] == case: - return compose_format(case_dict['format']) - return None - - -if __name__ == '__main__': - fmt = get_format('functest', 'vping_ssh') - print fmt -- cgit 1.2.3-korg