From d0f40c056a9283f30df45da5d35b72ce79b5d9a0 Mon Sep 17 00:00:00 2001 From: Mark Beierl Date: Thu, 12 May 2016 10:05:25 -0600 Subject: Results DB Reporting Add the ability to push results to the test result db. If the environment variable TEST_DB_URL is defined, results will be pushed there. If not, no push is attempted. Change-Id: Ib833530d7379c5f37f0d2904a83d31a4ee559ae6 JIRA: STORPERF-13 Signed-off-by: Mark Beierl --- storperf/db/job_db.py | 7 ++- storperf/db/test_results_db.py | 56 ++++++++++++++++++++++++ storperf/plot/barchart.py | 2 +- storperf/storperf_master.py | 5 +-- storperf/test_executor.py | 68 +++++++++++++++++++++++++++-- storperf/tests/db_tests/graphite_db_test.py | 14 ++++-- storperf/tests/db_tests/job_db_test.py | 7 +-- storperf/tests/utilities/__init__.py | 8 ++++ storperf/tests/utilities/dictionary.py | 42 ++++++++++++++++++ storperf/utilities/__init__.py | 8 ++++ storperf/utilities/dictionary.py | 15 +++++++ 11 files changed, 217 insertions(+), 15 deletions(-) create mode 100644 storperf/db/test_results_db.py create mode 100644 storperf/tests/utilities/__init__.py create mode 100644 storperf/tests/utilities/dictionary.py create mode 100644 storperf/utilities/__init__.py create mode 100644 storperf/utilities/dictionary.py diff --git a/storperf/db/job_db.py b/storperf/db/job_db.py index 412c6bc..3d66be8 100644 --- a/storperf/db/job_db.py +++ b/storperf/db/job_db.py @@ -201,9 +201,12 @@ class JobDB(object): return workload_executions - def record_workload_params(self, job_id, params): + def record_workload_params(self, params): """ """ + if (self.job_id is None): + self.create_job_id() + with db_mutex: db = sqlite3.connect(JobDB.db_name) @@ -215,7 +218,7 @@ class JobDB(object): param, value) values (?, ?, ?)""", - (job_id, + (self.job_id, param, value,)) db.commit() diff --git a/storperf/db/test_results_db.py b/storperf/db/test_results_db.py new file mode 100644 index 0000000..8636e52 --- /dev/null +++ b/storperf/db/test_results_db.py @@ -0,0 +1,56 @@ +############################################################################## +# Copyright (c) 2016 EMC 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 +############################################################################## + +import json +import requests +import os + + +def get_installer_type(logger=None): + """ + Get installer type (fuel, apex, joid, compass) + """ + try: + installer = os.environ['INSTALLER_TYPE'] + except KeyError: + if logger: + logger.error("Impossible to retrieve the installer type") + installer = "Unknown_installer" + + return installer + + +def push_results_to_db(db_url, project, case_name, logger, pod_name, + version, scenario, criteria, build_tag, payload): + """ + POST results to the Result target DB + """ + url = db_url + "/results" + installer = get_installer_type(logger) + params = {"project_name": project, "case_name": case_name, + "pod_name": pod_name, "installer": installer, + "version": version, "scenario": scenario, "criteria": criteria, + "build_tag": build_tag, "details": payload} + + headers = {'Content-Type': 'application/json'} + try: + if logger: + logger.debug("Pushing results to %s" % (url)) + r = requests.post(url, data=json.dumps(params), headers=headers) + if logger: + logger.debug(r) + logger.debug(r.status_code) + logger.debug(r.content) + return True + except Exception, e: + logger.error("Error [push_results_to_db('%s', '%s', '%s', " + + "'%s', '%s', '%s', '%s', '%s', '%s')]:" % + (db_url, project, case_name, pod_name, version, + scenario, criteria, build_tag, payload), e) + return False diff --git a/storperf/plot/barchart.py b/storperf/plot/barchart.py index 871defa..6fcfd8d 100644 --- a/storperf/plot/barchart.py +++ b/storperf/plot/barchart.py @@ -1,5 +1,5 @@ ############################################################################## -# Copyright (c) 2015 EMC and others. +# Copyright (c) 2016 EMC and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 diff --git a/storperf/storperf_master.py b/storperf/storperf_master.py index 0f86a95..395a2ab 100644 --- a/storperf/storperf_master.py +++ b/storperf/storperf_master.py @@ -1,5 +1,5 @@ ############################################################################## -# Copyright (c) 2015 EMC and others. +# Copyright (c) 2016 EMC and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -312,13 +312,12 @@ class StorPerfMaster(object): thread.join() self._test_executor.slaves = slaves - job_id = self._test_executor.execute() params = metadata params['agent_count'] = self.agent_count params['public_network'] = self.public_network params['volume_size'] = self.volume_size - self.job_db.record_workload_params(job_id, params) + job_id = self._test_executor.execute(params) return job_id diff --git a/storperf/test_executor.py b/storperf/test_executor.py index 8230174..592c33d 100644 --- a/storperf/test_executor.py +++ b/storperf/test_executor.py @@ -1,5 +1,5 @@ ############################################################################## -# Copyright (c) 2015 EMC and others. +# Copyright (c) 2016 EMC and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -11,8 +11,11 @@ from os import listdir from os.path import isfile, join from storperf.carbon.converter import Converter from storperf.carbon.emitter import CarbonMetricTransmitter +from storperf.db import test_results_db +from storperf.db.graphite_db import GraphiteDB from storperf.db.job_db import JobDB from storperf.fio.fio_invoker import FIOInvoker +from storperf.utilities import dictionary from threading import Thread import copy import imp @@ -35,6 +38,7 @@ class TestExecutor(object): self.precondition = True self.deadline = None self.warm = True + self.metadata = None self._queue_depths = [1, 4, 8] self._block_sizes = [512, 4096, 16384] self.event_listeners = set() @@ -138,9 +142,12 @@ class TestExecutor(object): return imp.load_source(mname, no_ext + '.py') return None - def execute(self): + def execute(self, metadata): self.job_db.create_job_id() - self._workload_thread = Thread(target=self.execute_workloads, args=()) + self.job_db.record_workload_params(metadata) + self.metadata = metadata + self._workload_thread = Thread(target=self.execute_workloads, + args=()) self._workload_thread.start() return self.job_db.job_id @@ -158,8 +165,14 @@ class TestExecutor(object): def execute_workloads(self): self._terminated = False + self.logger.info("Starting job %s" % (self.job_db.job_id)) + + start_time = time.time() + for workload_module in self.workload_modules: workload_name = getattr(workload_module, "__name__") + self.logger.info("Starting workload %s" % (workload_name)) + constructorMethod = getattr(workload_module, workload_name) workload = constructorMethod() if (self.filename is not None): @@ -215,6 +228,55 @@ class TestExecutor(object): self._workload_executors = [] + self.logger.info("Completed workload %s" % (workload_name)) + self.logger.info("Completed job %s" % (self.job_db.job_id)) + end_time = time.time() + pod_name = dictionary.get_key_from_dict(self.metadata, + 'pod_name', + 'Unknown') + version = dictionary.get_key_from_dict(self.metadata, + 'version', + 'Unknown') + scenario = dictionary.get_key_from_dict(self.metadata, + 'scenario', + 'Unknown') + build_tag = dictionary.get_key_from_dict(self.metadata, + 'build_tag', + 'Unknown') + duration = end_time - start_time + test_db = os.environ.get('TEST_DB_URL') + + if test_db is not None: + # I really do not like doing this. As our threads just + # terminated, their final results are still being spooled + # off to Carbon. Need to give that a little time to finish + time.sleep(5) + self.logger.info("Pushing results to %s" % (test_db)) + + payload = self.metadata + payload['timestart'] = start_time + payload['duration'] = duration + payload['status'] = 'OK' + graphite_db = GraphiteDB() + payload['metrics'] = graphite_db.fetch_averages(self.job_db.job_id) + criteria = {} + criteria['block_sizes'] = self.block_sizes + criteria['queue_depths'] = self.block_sizes + + try: + test_results_db.push_results_to_db(test_db, + "storperf", + "Latency Test", + self.logger, + pod_name, + version, + scenario, + criteria, + build_tag, + payload) + except: + self.logger.exception("Error pushing results into Database") + def execute_on_node(self, workload): invoker = FIOInvoker() diff --git a/storperf/tests/db_tests/graphite_db_test.py b/storperf/tests/db_tests/graphite_db_test.py index ce970bb..e13545b 100644 --- a/storperf/tests/db_tests/graphite_db_test.py +++ b/storperf/tests/db_tests/graphite_db_test.py @@ -1,5 +1,13 @@ +############################################################################## +# Copyright (c) 2016 EMC 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 +############################################################################## + from storperf.db.graphite_db import GraphiteDB -import this import unittest @@ -9,13 +17,13 @@ class GraphiteDBTest(unittest.TestCase): self.graphdb = GraphiteDB() self.graphdb._job_db = self - def test_wilcard_pattern(self): + def test_wildcard_pattern(self): workload = "job_id" expected = "job_id.*.*.*.*.*.*" actual = self.graphdb.make_fullname_pattern(workload) self.assertEqual(expected, actual, actual) - def test_no_wilcard_pattern(self): + def test_no_wildcard_pattern(self): workload = "job_id.workload.host.queue-depth.1.block-size.16" actual = self.graphdb.make_fullname_pattern(workload) self.assertEqual(workload, actual, actual) diff --git a/storperf/tests/db_tests/job_db_test.py b/storperf/tests/db_tests/job_db_test.py index 0972f84..fe3d9f1 100644 --- a/storperf/tests/db_tests/job_db_test.py +++ b/storperf/tests/db_tests/job_db_test.py @@ -1,5 +1,5 @@ ############################################################################## -# Copyright (c) 2015 EMC and others. +# Copyright (c) 2016 EMC and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -186,6 +186,7 @@ class JobDBTest(unittest.TestCase): def test_job_params(self): expected = {"a": "1", "b": "2"} - self.job.record_workload_params("ABCD", expected) - actual = self.job.fetch_workload_params("ABCD") + self.job.job_id = "ABCD" + self.job.record_workload_params(expected) + actual = self.job.fetch_workload_params(self.job.job_id) self.assertEqual(expected, actual) diff --git a/storperf/tests/utilities/__init__.py b/storperf/tests/utilities/__init__.py new file mode 100644 index 0000000..73444b6 --- /dev/null +++ b/storperf/tests/utilities/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2016 EMC 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 +############################################################################## diff --git a/storperf/tests/utilities/dictionary.py b/storperf/tests/utilities/dictionary.py new file mode 100644 index 0000000..0819cef --- /dev/null +++ b/storperf/tests/utilities/dictionary.py @@ -0,0 +1,42 @@ +############################################################################## +# Copyright (c) 2016 EMC 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 +############################################################################## + +import unittest +from storperf.utilities import dictionary + + +class DictionaryTest(unittest.TestCase): + + def setUp(self): + self.dictionary = {} + self.dictionary['key'] = 'value' + pass + + def test_get_no_default(self): + expected = None + actual = dictionary.get_key_from_dict(self.dictionary, 'no-key') + self.assertEqual(expected, actual) + + def test_get_with_default(self): + expected = 'value 2' + actual = dictionary.get_key_from_dict( + self.dictionary, 'no-key', expected) + self.assertEqual(expected, actual) + + def test_get_with_value(self): + expected = 'value' + actual = dictionary.get_key_from_dict( + self.dictionary, 'key') + self.assertEqual(expected, actual) + + def test_get_with_value_and_default(self): + expected = 'value' + actual = dictionary.get_key_from_dict( + self.dictionary, 'key', 'value 2') + self.assertEqual(expected, actual) diff --git a/storperf/utilities/__init__.py b/storperf/utilities/__init__.py new file mode 100644 index 0000000..73444b6 --- /dev/null +++ b/storperf/utilities/__init__.py @@ -0,0 +1,8 @@ +############################################################################## +# Copyright (c) 2016 EMC 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 +############################################################################## diff --git a/storperf/utilities/dictionary.py b/storperf/utilities/dictionary.py new file mode 100644 index 0000000..95f625c --- /dev/null +++ b/storperf/utilities/dictionary.py @@ -0,0 +1,15 @@ +############################################################################## +# Copyright (c) 2016 EMC 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 +############################################################################## + + +def get_key_from_dict(dictionary, key, default_value=None): + if key in dictionary: + return dictionary[key] + else: + return default_value -- cgit 1.2.3-korg