browbeat/lib/PerfKit.py
akrzos 03f4db089e Index Perfkit Result Data into ES
This commit adds indexing perfkit result data into ElasticSearch.
The results will be able to be aggregated and visualized over various
points of metadata.

+ Improved creating a result directory to help support testing PerfKit
  indexing with pre-loaded results from previous runs.
+ Add additional error logging when unable to index results.

Change-Id: Ic18b43f19d75a061c117de6ba41545c68792e27c
2016-10-10 12:43:14 -04:00

238 lines
11 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import Connmon
import datetime
import Elastic
import glob
import Grafana
import logging
import os
import shutil
import subprocess
import time
import Tools
import WorkloadBase
class PerfKit(WorkloadBase.WorkloadBase):
def __init__(self, config):
self.logger = logging.getLogger('browbeat.PerfKit')
self.config = config
self.error_count = 0
self.tools = Tools.Tools(self.config)
self.connmon = Connmon.Connmon(self.config)
self.grafana = Grafana.Grafana(self.config)
self.elastic = Elastic.Elastic(self.config, self.__class__.__name__.lower())
self.test_count = 0
self.scenario_count = 0
self.pass_count = 0
def _log_details(self):
self.logger.info(
"Current number of Perkit scenarios executed: {}".format(self.scenario_count))
self.logger.info(
"Current number of Perfkit test(s) executed: {}".format(self.test_count))
self.logger.info(
"Current number of Perfkit test(s) succeeded: {}".format(self.pass_count))
self.logger.info(
"Current number of Perfkit test failures: {}".format(self.error_count))
def string_to_dict(self, string):
"""Function for converting "|" quoted hash data into python dictionary."""
dict_data = {}
split_data = string.split('|,|')
split_data[0] = split_data[0][1:]
split_data[-1] = split_data[-1][:-1]
for item in split_data:
split_item = item.replace('.', '_').split(':')
dict_data[split_item[0]] = split_item[1]
return dict_data
def update_tests(self):
self.test_count += 1
def update_pass_tests(self):
self.pass_count += 1
def update_fail_tests(self):
self.error_count += 1
def update_scenarios(self):
self.scenario_count += 1
def get_error_details(self, result_dir):
error_details = []
with open('{}/pkb.stderr.log'.format(result_dir)) as perfkit_stderr:
for line in perfkit_stderr:
if 'ERROR' in line or 'Error' in line or 'Exception' in line:
error_details.append(line)
return error_details
def index_results(self, sucessful_run, result_dir, test_name, browbeat_rerun, benchmark_config):
complete_result_json = {'browbeat_scenario': benchmark_config}
es_ts = datetime.datetime.utcnow()
if sucessful_run:
complete_result_json['results'] = {'unit':{}, 'value': {}}
# PerfKit json is newline delimited and thus each newline json needs to be loaded
with open('{}/perfkitbenchmarker_results.json'.format(result_dir)) \
as perfkit_results_json:
for json_result in perfkit_results_json:
single_result = self.elastic.load_json(json_result.strip())
if 'browbeat_rerun' not in complete_result_json:
complete_result_json['browbeat_rerun'] = browbeat_rerun
if 'timestamp' not in complete_result_json:
complete_result_json['timestamp'] = str(es_ts).replace(" ", "T")
if 'grafana_url' not in complete_result_json:
complete_result_json['grafana_url'] = self.grafana.grafana_urls()
if 'perfkit_setup' not in complete_result_json:
complete_result_json['perfkit_setup'] = \
self.string_to_dict(single_result['labels'])
result_metric = single_result['metric'].lower().replace(' ', '_'). \
replace('.', '_')
complete_result_json['results']['value'][result_metric] = single_result['value']
complete_result_json['results']['unit'][result_metric] = single_result['unit']
result_type = 'result'
else:
complete_result_json['perfkit_errors'] = self.get_error_details(result_dir)
complete_result_json['browbeat_rerun'] = browbeat_rerun
complete_result_json['timestamp'] = str(es_ts).replace(" ", "T")
complete_result_json['grafana_url'] = self.grafana.grafana_urls()
result_type = 'error'
result = self.elastic.combine_metadata(complete_result_json)
self.elastic.index_result(result, test_name, result_type)
def run_benchmark(self, benchmark_config, result_dir, test_name, cloud_type="OpenStack"):
self.logger.debug("--------------------------------")
self.logger.debug("Benchmark_config: {}".format(benchmark_config))
self.logger.debug("result_dir: {}".format(result_dir))
self.logger.debug("test_name: {}".format(test_name))
self.logger.debug("--------------------------------")
# Build command to run
if 'enabled' in benchmark_config:
del benchmark_config['enabled']
cmd = ("source /home/stack/overcloudrc; source {0}; "
"/home/stack/perfkit-venv/PerfKitBenchmarker/pkb.py "
"--cloud={1} --run_uri=browbeat".format(self.config['perfkit']['venv'], cloud_type))
for parameter, value in benchmark_config.iteritems():
if not parameter == 'name':
self.logger.debug(
"Parameter: {}, Value: {}".format(parameter, value))
cmd += " --{}={}".format(parameter, value)
# Remove any old results
if os.path.exists("/tmp/perfkitbenchmarker/runs/browbeat"):
shutil.rmtree("/tmp/perfkitbenchmarker/runs/browbeat")
if self.config['connmon']['enabled']:
self.connmon.start_connmon()
self.logger.info("Running Perfkit Command: {}".format(cmd))
stdout_file = open("{}/pkb.stdout.log".format(result_dir), 'w')
stderr_file = open("{}/pkb.stderr.log".format(result_dir), 'w')
from_ts = time.time()
if 'sleep_before' in self.config['perfkit']:
time.sleep(self.config['perfkit']['sleep_before'])
process = subprocess.Popen(
cmd, shell=True, stdout=stdout_file, stderr=stderr_file)
process.communicate()
if 'sleep_after' in self.config['perfkit']:
time.sleep(self.config['perfkit']['sleep_after'])
to_ts = time.time()
# Stop connmon at end of perfkit task
if self.config['connmon']['enabled']:
self.connmon.stop_connmon()
try:
self.connmon.move_connmon_results(result_dir, test_name)
self.connmon.connmon_graphs(result_dir, test_name)
except Exception:
self.logger.error(
"Connmon Result data missing, Connmon never started")
workload = self.__class__.__name__
new_test_name = test_name.split('-')
new_test_name = new_test_name[2:]
new_test_name = '-'.join(new_test_name)
# Determine success
success = False
try:
with open("{}/pkb.stderr.log".format(result_dir), 'r') as stderr:
if any('SUCCEEDED' in line for line in stderr):
self.logger.info("Benchmark completed.")
self.update_pass_tests()
self.update_total_pass_tests()
self.get_time_dict(to_ts, from_ts, benchmark_config['benchmarks'],
new_test_name, workload, "pass")
success = True
else:
self.logger.error("Benchmark failed.")
self.update_fail_tests()
self.update_total_fail_tests()
self.get_time_dict(to_ts, from_ts, benchmark_config['benchmarks'],
new_test_name, workload, "fail")
except IOError:
self.logger.error(
"File missing: {}/pkb.stderr.log".format(result_dir))
# Copy all results
for perfkit_file in glob.glob("/tmp/perfkitbenchmarker/runs/browbeat/*"):
shutil.move(perfkit_file, result_dir)
if os.path.exists("/tmp/perfkitbenchmarker/runs/browbeat"):
shutil.rmtree("/tmp/perfkitbenchmarker/runs/browbeat")
# Grafana integration
self.grafana.create_grafana_urls(
{'from_ts': int(from_ts * 1000),
'to_ts': int(to_ts * 1000)})
self.grafana.print_dashboard_url(test_name)
self.grafana.log_snapshot_playbook_cmd(
from_ts, to_ts, result_dir, test_name)
self.grafana.run_playbook(from_ts, to_ts, result_dir, test_name)
return success
def start_workloads(self):
self.logger.info("Starting PerfKitBenchmarker Workloads.")
time_stamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S")
self.logger.debug("Time Stamp (Prefix): {}".format(time_stamp))
benchmarks = self.config.get('perfkit')['benchmarks']
if (benchmarks is not None and len(benchmarks) > 0):
for benchmark in benchmarks:
if benchmark['enabled']:
self.logger.info("Benchmark: {}".format(benchmark['name']))
self.update_scenarios()
self.update_total_scenarios()
# Add default parameters as necessary
for default_item, value in self.config['perfkit']['default'].iteritems():
if default_item not in benchmark:
benchmark[default_item] = value
for run in range(self.config['browbeat']['rerun']):
self.update_tests()
self.update_total_tests()
result_dir = self.tools.create_results_dir(
self.config['browbeat']['results'], time_stamp, benchmark['name'], run)
test_name = "{}-{}-{}".format(time_stamp, benchmark['name'], run)
workload = self.__class__.__name__
self.workload_logger(result_dir, workload)
sucess = self.run_benchmark(benchmark, result_dir, test_name)
self._log_details()
if self.config['elasticsearch']['enabled']:
self.index_results(sucess, result_dir, test_name, run, benchmark)
else:
self.logger.info(
"Skipping {} benchmark, enabled: false".format(benchmark['name']))
else:
self.logger.error("Config file contains no perfkit benchmarks.")