Adding Browbeat Infra files
Adding .gitreview file Adding tox.ini Adding setup.py Adding test-requirments.txt Fixing syntax Change-Id: Id6d628708079440207e5f068f5f0827802f2aa14
This commit is contained in:
parent
c838c61ba2
commit
06e3e3292b
@ -1,5 +1,4 @@
|
||||
[gerrit]
|
||||
host=review.gerrithub.io
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=jtaleric/browbeat
|
||||
defaultbranch=master
|
||||
project=openstack/browbeat.git
|
||||
|
@ -1,3 +1,15 @@
|
||||
# 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.
|
||||
|
||||
def dict_remove(the_dict, item):
|
||||
"""Remove an item from a dictionary."""
|
||||
del the_dict[item]
|
||||
|
12
ansible/install/roles/dashboard-openstack/fix-ids.py
Executable file → Normal file
12
ansible/install/roles/dashboard-openstack/fix-ids.py
Executable file → Normal file
@ -1,3 +1,15 @@
|
||||
# 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.
|
||||
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import json
|
||||
|
12
browbeat.py
Executable file → Normal file
12
browbeat.py
Executable file → Normal file
@ -1,4 +1,16 @@
|
||||
#!/usr/bin/env 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.
|
||||
|
||||
from lib.PerfKit import PerfKit
|
||||
from lib.Rally import Rally
|
||||
from lib.Shaker import Shaker
|
||||
|
73
docs/source/conf.py
Normal file
73
docs/source/conf.py
Normal file
@ -0,0 +1,73 @@
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
# 'sphinx.ext.intersphinx',
|
||||
'oslosphinx'
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'browbeat'
|
||||
copyright = u'2013, OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
# intersphinx_mapping = {'http://docs.python.org/': None}
|
4
docs/source/contributing.rst
Normal file
4
docs/source/contributing.rst
Normal file
@ -0,0 +1,4 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
.. include:: ../../CONTRIBUTING.rst
|
25
docs/source/index.rst
Normal file
25
docs/source/index.rst
Normal file
@ -0,0 +1,25 @@
|
||||
.. browbeat documentation master file, created by
|
||||
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to browbeat's documentation!
|
||||
========================================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
readme
|
||||
installation
|
||||
usage
|
||||
contributing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
3
docs/source/installation.rst
Normal file
3
docs/source/installation.rst
Normal file
@ -0,0 +1,3 @@
|
||||
============
|
||||
Installation
|
||||
============
|
1
docs/source/readme.rst
Normal file
1
docs/source/readme.rst
Normal file
@ -0,0 +1 @@
|
||||
.. include:: ../../README.rst
|
3
docs/source/usage.rst
Normal file
3
docs/source/usage.rst
Normal file
@ -0,0 +1,3 @@
|
||||
========
|
||||
Usage
|
||||
========
|
@ -1,199 +0,0 @@
|
||||
import csv
|
||||
from collections import Counter
|
||||
import sys
|
||||
from datetime import datetime
|
||||
import matplotlib
|
||||
import numpy as np
|
||||
import ntpath
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.cbook as cbook
|
||||
from pylab import rcParams
|
||||
rcParams['figure.figsize'] = 18, 10
|
||||
|
||||
services=['/usr/bin/nova-scheduler','/usr/bin/keystone-all','/usr/bin/nova-api',
|
||||
'/usr/bin/nova-conductor','/usr/bin/neutron-server','/usr/bin/cinder-api',
|
||||
'/usr/bin/cinder-volume','/usr/bin/cinder-scheduler']
|
||||
color_wheel=['r','g','b','y']
|
||||
|
||||
data = {}
|
||||
average = {}
|
||||
for service in services :
|
||||
data[service] = {}
|
||||
average[service] = {}
|
||||
average[service]['connection_count_avg'] = 0
|
||||
average[service]['max_checkedout_avg'] = 0
|
||||
average[service]['checkouts_per_second_avg'] = 0
|
||||
|
||||
print "--------------------------------------------------------------------------------------"
|
||||
print "Reading File : %s" % sys.argv[1]
|
||||
print "--------------------------------------------------------------------------------------"
|
||||
reader = csv.DictReader(open(sys.argv[1]))
|
||||
for row in reader:
|
||||
for service in services :
|
||||
if not row['hostname'] in data[service].keys() :
|
||||
data[service][row['hostname']] = {}
|
||||
data[service][row['hostname']]['timestamp'] = []
|
||||
data[service][row['hostname']]['hostname'] = []
|
||||
data[service][row['hostname']]['max_connections'] = []
|
||||
data[service][row['hostname']]['checkout_count'] = []
|
||||
data[service][row['hostname']]['connection_count'] = []
|
||||
data[service][row['hostname']]['max_checkedout'] = []
|
||||
data[service][row['hostname']]['checkouts_per_second'] = []
|
||||
|
||||
if row['progname'] == service :
|
||||
data[service][row['hostname']]['timestamp'].append(datetime.strptime(row['timestamp'],
|
||||
'%Y-%m-%d %H:%M:%S'))
|
||||
data[service][row['hostname']]['connection_count'].append(row['connection_count'])
|
||||
data[service][row['hostname']]['max_connections'].append(row['max_connections'])
|
||||
data[service][row['hostname']]['checkout_count'].append(row['checkout_count'])
|
||||
data[service][row['hostname']]['max_checkedout'].append(row['max_checkedout'])
|
||||
data[service][row['hostname']]['checkouts_per_second'].append(row['checkouts_per_second'])
|
||||
|
||||
#
|
||||
# Graph connections across each controller.
|
||||
#
|
||||
for service in data :
|
||||
|
||||
print "Building Graph of connections per host second for : %s" % service
|
||||
plt.title("Database Connections : Service : %s" % service)
|
||||
plt.xlabel("Time")
|
||||
plt.ylabel("Connections")
|
||||
pos=0
|
||||
for host in data[service] :
|
||||
controller,=plt.plot_date(data[service][host]['timestamp'],
|
||||
data[service][host]['connection_count'],
|
||||
'c',
|
||||
linewidth=5,label="%s-controller0-conn"%service)
|
||||
|
||||
controller2,=plt.plot_date(data[service][host]['timestamp'],
|
||||
data[service][host]['checkout_count'],
|
||||
'c',
|
||||
linewidth=3,
|
||||
label="%s-controller0-ckout"%service)
|
||||
|
||||
controller1,=plt.plot_date(data[service][host]['timestamp'],
|
||||
data[service][host]['max_checkedout'],
|
||||
'c',
|
||||
linewidth=1,
|
||||
label="%s-controller0-max_checkedout"%service)
|
||||
|
||||
controller.set_color(color_wheel[pos])
|
||||
controller1.set_color(color_wheel[pos])
|
||||
controller2.set_color(color_wheel[pos])
|
||||
pos=pos+1
|
||||
|
||||
plt.legend(["%s-controller0-conn"%service,
|
||||
"%s-controller0-ckout"%service,
|
||||
"%s-controller0-max-ckout"%service,
|
||||
"%s-controller1-conn"%service,
|
||||
"%s-controller1-ckout"%service,
|
||||
"%s-controller1-max-ckout"%service,
|
||||
"%s-controller2-conn"%service,
|
||||
"%s-controller2-ckout"%service,
|
||||
"%s-controller2-max-ckout"%service])
|
||||
|
||||
plt.savefig("%s_%s-connctions.png"%(sys.argv[1],ntpath.basename(service)), bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
#
|
||||
# Graph checkouts per second across each controller.
|
||||
#
|
||||
print "Building Graph of checkouts per second for : %s" % service
|
||||
pos=0
|
||||
for host in data[service] :
|
||||
plt.title("Database Checkouts Per-Second : Service : %s" % service)
|
||||
plt.xlabel("Time")
|
||||
plt.ylabel("Connections")
|
||||
|
||||
controller,=plt.plot_date(data[service][host]['timestamp'],
|
||||
data[service][host]['checkouts_per_second'],
|
||||
'c',
|
||||
linewidth=1,
|
||||
label="%s-controller0-ckout"%service)
|
||||
|
||||
controller.set_color(color_wheel[pos])
|
||||
pos=pos+1
|
||||
|
||||
plt.legend(["%s-controller0-ckout-persec"%service,
|
||||
"%s-controller1-ckout-persec"%service,
|
||||
"%s-controller2-ckout-persec"%service])
|
||||
plt.savefig("%s_%s-connctions-checkout-persec.png"%
|
||||
(sys.argv[1],
|
||||
ntpath.basename(service)),
|
||||
bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
#
|
||||
# Sum connections across controllers
|
||||
#
|
||||
#
|
||||
print "Building Graph of sum of connections for : %s" % service
|
||||
num_controllers=len(data[service].keys())
|
||||
pos=0
|
||||
total_connections = np.array([])
|
||||
total_checkouts = np.array([])
|
||||
total_maxcheckouts = np.array([])
|
||||
for host in data[service] :
|
||||
plt.title("Database Connections : Service : %s" % service)
|
||||
plt.xlabel("Time")
|
||||
plt.ylabel("Connections")
|
||||
if pos == 0 :
|
||||
total_connections = np.array(data[service][host]['connection_count']).astype(np.float)
|
||||
total_checkouts = np.array(data[service][host]['checkout_count']).astype(np.float)
|
||||
total_maxcheckouts = np.array(data[service][host]['max_checkedout']).astype(np.float)
|
||||
|
||||
elif pos <= num_controllers :
|
||||
if total_connections.size < len(data[service][host]['connection_count']):
|
||||
data[service][host]['connection_count'] = np.resize(data[service][host]['connection_count'],total_connections.size)
|
||||
else:
|
||||
total_connections = np.resize(total_connections,len(data[service][host]['connection_count']))
|
||||
|
||||
if total_checkouts.size < len(data[service][host]['checkout_count']):
|
||||
data[service][host]['checkout_count'] = np.resize(data[service][host]['checkout_count'],total_checkouts.size)
|
||||
else:
|
||||
total_checkouts = np.resize(total_checkouts,len(data[service][host]['checkout_count']))
|
||||
|
||||
if total_maxcheckouts.size < len(data[service][host]['max_checkedout']):
|
||||
data[service][host]['max_checkedout'] = np.resize(data[service][host]['max_checkedout'],total_maxcheckouts.size)
|
||||
else:
|
||||
total_maxcheckouts = np.resize(total_maxcheckouts,len(data[service][host]['max_checkedout']))
|
||||
|
||||
total_connections = np.add(total_connections, np.array(data[service][host]['connection_count']).astype(np.float))
|
||||
total_checkouts= np.add(total_checkouts, np.array(data[service][host]['checkout_count']).astype(np.float))
|
||||
total_maxcheckouts= np.add(total_maxcheckouts, np.array(data[service][host]['max_checkedout']).astype(np.float))
|
||||
|
||||
pos=pos+1
|
||||
|
||||
plt.title("Database Connections : Service : %s" % service)
|
||||
plt.xlabel("Time")
|
||||
plt.ylabel("Connections")
|
||||
pos=0
|
||||
controller,=plt.plot_date(np.resize(data[service][host]['timestamp'],len(total_connections)),
|
||||
total_connections,
|
||||
'c',
|
||||
linewidth=5,label="%s-controllers-conn"%service)
|
||||
|
||||
controller2,=plt.plot_date(np.resize(data[service][host]['timestamp'],len(total_checkouts)),
|
||||
total_checkouts,
|
||||
'c',
|
||||
linewidth=3,
|
||||
label="%s-controllers-ckout"%service)
|
||||
|
||||
controller1,=plt.plot_date(np.resize(data[service][host]['timestamp'],len(total_maxcheckouts)),
|
||||
total_maxcheckouts,
|
||||
'c',
|
||||
linewidth=1,
|
||||
label="%s-controllers-max_checkedout"%service)
|
||||
|
||||
controller.set_color(color_wheel[pos])
|
||||
controller1.set_color(color_wheel[pos+1])
|
||||
controller2.set_color(color_wheel[pos+2])
|
||||
|
||||
plt.legend(["%s-controllers-sum-conn"%service,
|
||||
"%s-controllers-sum-ckout"%service,
|
||||
"%s-controllers-sum-maxckout"%service])
|
||||
|
||||
plt.savefig("%s_%s-connctions-all.png"%(sys.argv[1],ntpath.basename(service)), bbox_inches='tight')
|
||||
plt.close()
|
||||
|
@ -1,175 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from datetime import datetime
|
||||
from collections import OrderedDict
|
||||
import argparse
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Saved Measurements:
|
||||
measurements = ['Min', 'Median', '90%ile', '95%ile', 'Max', 'Avg', 'Success%', 'Count']
|
||||
|
||||
"""
|
||||
Results directory structure:
|
||||
".../browbeat/results/full-apache-fernet-keystone-36/keystone/keystone-cc/run-1/
|
||||
full-apache-fernet-keystone-36-iteration_1-keystone-cc-0256.log"
|
||||
Structure of compiled results dictionary:
|
||||
results[service][test][iteration][#workers][concurrency][measurement] = value
|
||||
"""
|
||||
|
||||
def list_only_directories(the_directory):
|
||||
return [a_dir for a_dir in os.listdir(the_directory)
|
||||
if os.path.isdir(os.path.join(the_directory, a_dir)) ]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Processes multiple rally log files from brwowbeat into compiled graphs.')
|
||||
parser.add_argument('test_prefix', help='Use the resulting prefixed directories/files in '
|
||||
'browbeat results directory.')
|
||||
args = parser.parse_args()
|
||||
|
||||
compiled_results = OrderedDict()
|
||||
compiled_issues = []
|
||||
# Should be /home/<user>/browbeat/graphing:
|
||||
rallyplot_path = os.path.dirname(os.path.realpath(__file__))
|
||||
browbeat_path = rallyplot_path.replace('/graphing', '')
|
||||
|
||||
test_runs = [a_dir for a_dir in list_only_directories('{}/results/'.format(browbeat_path))
|
||||
if re.match('^{}-[A-Za-z]+-[0-9]+'.format(args.test_prefix), a_dir)]
|
||||
|
||||
for test_run in test_runs:
|
||||
extract = re.search('{}-([a-zA-Z]*)-([0-9]*)'.format(args.test_prefix), test_run)
|
||||
skip = True
|
||||
if extract:
|
||||
service = extract.group(1)
|
||||
w_count = extract.group(2)
|
||||
skip = False
|
||||
else:
|
||||
print 'Potentially incorrect directory: {}'.format(test_run)
|
||||
if not skip:
|
||||
for service in os.listdir('{}/results/{}/'.format(browbeat_path, test_run)):
|
||||
if service not in compiled_results:
|
||||
compiled_results[service] = OrderedDict()
|
||||
for test in os.listdir('{}/results/{}/{}/'.format(browbeat_path, test_run, service)):
|
||||
if test not in compiled_results[service]:
|
||||
compiled_results[service][test] = OrderedDict()
|
||||
for iteration in os.listdir('{}/results/{}/{}/{}/'.format(browbeat_path, test_run, service, test)):
|
||||
iter_num = int(iteration.replace('run-', ''))
|
||||
if iter_num not in compiled_results[service][test]:
|
||||
compiled_results[service][test][iter_num] = OrderedDict()
|
||||
if w_count not in compiled_results[service][test][iter_num]:
|
||||
compiled_results[service][test][iter_num][w_count] = OrderedDict()
|
||||
result_files = os.listdir('{}/results/{}/{}/{}/{}/'.format(browbeat_path, test_run, service, test, iteration))
|
||||
result_files = [a_file for a_file in result_files if re.match('.*log', a_file)]
|
||||
for r_file in result_files:
|
||||
# Extract concurrency of test
|
||||
extract = re.search('{}-{}-{}-iteration_{}-{}-([0-9]*)\.log'.format(args.test_prefix, service, w_count, iter_num, test), r_file)
|
||||
if extract:
|
||||
concurrency = extract.group(1)
|
||||
if concurrency not in compiled_results[service][test][iter_num][w_count]:
|
||||
compiled_results[service][test][iter_num][w_count][concurrency] = OrderedDict()
|
||||
result_file_full_path = '{}/results/{}/{}/{}/{}/{}'.format(browbeat_path, test_run, service, test, iteration, r_file)
|
||||
# print 'Test_run: {}, Service: {}, Test: {}, iteration: {}, Concurrency: {}, Result_file: {}'.format(test_run, service, test, iteration, concurrency, r_file)
|
||||
# print 'Full Path: {}'.format(result_file_full_path)
|
||||
|
||||
grep_cmd = subprocess.Popen(['grep', 'total', result_file_full_path],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out, err = grep_cmd.communicate()
|
||||
if len(out) == 0:
|
||||
print 'Could not find results. Setting to -1'
|
||||
compiled_issues.append(r_file)
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Min'] = '-1'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Median'] = '-1'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['90%ile'] = '-1'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['95%ile'] = '-1'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Max'] = '-1'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Avg'] = '-1'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Success%'] = '0'
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Count'] = '-1'
|
||||
else:
|
||||
output = [s.strip() for s in out.strip().split('|') if s]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Min'] = output[1]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Median'] = output[2]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['90%ile'] = output[3]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['95%ile'] = output[4]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Max'] = output[5]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Avg'] = output[6]
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Success%'] = output[7].replace('%', '')
|
||||
compiled_results[service][test][iter_num][w_count][concurrency]['Count'] = output[8]
|
||||
|
||||
rally_graph_dir = '{}/results/{}-rally-compiled-graphs/'.format(browbeat_path, args.test_prefix)
|
||||
if not os.path.exists(rally_graph_dir):
|
||||
os.mkdir(rally_graph_dir)
|
||||
|
||||
# Now graph results based on measurements list:
|
||||
for service in compiled_results:
|
||||
for test in compiled_results[service]:
|
||||
# Assumption is all tests have same number of iterations!!!
|
||||
for iteration in compiled_results[service][test]:
|
||||
for measurement in measurements:
|
||||
concurrency_dict = {}
|
||||
for worker_count in sorted(compiled_results[service][test][iteration].keys()):
|
||||
for concurrency in compiled_results[service][test][iteration][worker_count]:
|
||||
if concurrency not in concurrency_dict:
|
||||
concurrency_dict[concurrency] = []
|
||||
if str(compiled_results[service][test][iteration][worker_count][concurrency][measurement]) == "n/a":
|
||||
# Rally will place n/a in place of an actual result when it fails
|
||||
# completely, we can't graph n/a, so replace with -1
|
||||
concurrency_dict[concurrency].append(-1)
|
||||
else:
|
||||
concurrency_dict[concurrency].append(float(compiled_results[service][test][iteration][worker_count][concurrency][measurement]))
|
||||
|
||||
graph_file_name = '{}{}-{}-{}-{}.png'.format(rally_graph_dir, service, test, iteration, measurement)
|
||||
print '----------------------------------------------------------'
|
||||
print 'Test Prefix: {}'.format(args.test_prefix)
|
||||
print 'Service: {}'.format(service)
|
||||
print 'Test: {}'.format(test)
|
||||
print 'Iteration: {}'.format(iteration)
|
||||
print 'Measurement: {}'.format(measurement)
|
||||
print 'File Name: {}'.format(graph_file_name)
|
||||
print 'X-Axis (Worker Counts): {}'.format(sorted(compiled_results[service][test][iteration].keys()))
|
||||
print 'X-Axis (# of values per series): {}'.format(len(compiled_results[service][test][iteration].keys()))
|
||||
print '# of Series (# of Concurrencies tested): {}'.format(len(compiled_results[service][test][iteration][worker_count].keys()))
|
||||
for series in sorted(concurrency_dict):
|
||||
print 'Series: {}, Values: {}'.format(series, concurrency_dict[series])
|
||||
print 'Legend: {}'.format(sorted(concurrency_dict.keys()))
|
||||
print '----------------------------------------------------------'
|
||||
fig = plt.figure()
|
||||
plt.title(
|
||||
'Test Name: {}\n'
|
||||
'Service: {}, Test: {}, Iteration: {}, Measurement: {}\n'
|
||||
'Graphed from rally task log output'.format(args.test_prefix, service, test,
|
||||
iteration, measurement))
|
||||
plt.xlabel('Workers')
|
||||
plt.ylabel('{} Time (s)'.format(measurement))
|
||||
ax = fig.add_subplot(111)
|
||||
for series in sorted(concurrency_dict.keys()):
|
||||
plt_linewidth = 1
|
||||
if '-1' in concurrency_dict[series]:
|
||||
plt_linewidth = 2
|
||||
plt.plot(sorted(compiled_results[service][test][iteration].keys()),
|
||||
concurrency_dict[series], linewidth=plt_linewidth, label=series, marker='o')
|
||||
for x, y in zip(sorted(compiled_results[service][test][iteration].keys()),
|
||||
concurrency_dict[series]):
|
||||
ax.annotate('%s' % y, xy=(x,y), xytext=(4,4), textcoords='offset points')
|
||||
plt.legend(loc='upper center', bbox_to_anchor=(1.12, 0.5), fancybox=True)
|
||||
ax.grid(True)
|
||||
plt.savefig(graph_file_name, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
# Print files that had an issue:
|
||||
print '----------------------------------------------------------'
|
||||
print 'Files missing results:'
|
||||
print '----------------------------------------------------------'
|
||||
for issue in compiled_issues:
|
||||
print 'File: {}'.format(issue)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,140 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import math
|
||||
import matplotlib.pyplot as plt
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
def getiter(prog_dict):
|
||||
density = prog_dict['deployment']['accommodation'][2]['density']
|
||||
compute = prog_dict['deployment']['accommodation'][3]['compute_nodes']
|
||||
iterval = density*compute
|
||||
if(prog_dict['deployment']['accommodation'][0]=="pair" and
|
||||
prog_dict['deployment']['accommodation'][1]=="single_room"):
|
||||
iterval //=2
|
||||
iterlist = []
|
||||
if prog_dict['execution']['progression'] in ['arithmetic', 'linear',
|
||||
'linear_progression']:
|
||||
iterlist = range(1,iterval+1)
|
||||
elif prog_dict['execution']['progression'] in ['geometric', 'quadratic',
|
||||
'quadratic_progression']:
|
||||
iterlist = [iterval]
|
||||
while iterval > 1:
|
||||
iterval //= 2
|
||||
iterlist.append(iterval)
|
||||
iterlist.reverse()
|
||||
elif prog_dict['execution']['progression'] == None:
|
||||
iterlist.append(iterval)
|
||||
return iterlist
|
||||
|
||||
|
||||
def get_uuidlist(data):
|
||||
uuidlist = []
|
||||
for key in data['records'].iterkeys():
|
||||
uuidlist.append(key)
|
||||
return uuidlist
|
||||
|
||||
def get_agentlist(uuidlist, data):
|
||||
agentset=set()
|
||||
for uuid in uuidlist:
|
||||
agentname = data['records'][uuid]['agent']
|
||||
agentset.add(agentname)
|
||||
agentlist = list(agentset)
|
||||
agentlist.sort()
|
||||
return agentlist
|
||||
|
||||
|
||||
def generate_aggregated_graphs(data, fname):
|
||||
for key in data['scenarios'].iterkeys():
|
||||
time1 = data['scenarios'][key]['execution']['tests'][0]['time']
|
||||
time = range(time1-1)
|
||||
density = (data['scenarios'][key]['deployment']
|
||||
['accommodation'][2]['density'])
|
||||
concur_list=getiter(data['scenarios'][key])
|
||||
uuidlist = get_uuidlist(data)
|
||||
title_name = (data['scenarios'][key]['title']).split('/')
|
||||
for concur in concur_list:
|
||||
countlist=[0]*(time1-1)
|
||||
for uuid in uuidlist:
|
||||
if data['records'][uuid]['concurrency'] == concur:
|
||||
if data['records'][uuid]['status'] == "ok":
|
||||
for index in range(time1-1):
|
||||
countlist[index] += ((data['records'][uuid]
|
||||
['samples'][index][1])/math.pow(10,6))
|
||||
plt.xlabel('Time in seconds')
|
||||
plt.ylabel('Throughput in Mbps')
|
||||
plt.title('Aggregated Throuhput for concurrencies \non node\n{}'.format(
|
||||
data['records'][uuid]['node']),loc='left')
|
||||
plt.title(title_name[10],loc='right')
|
||||
plt.plot(time,countlist, linewidth=1,marker='o',
|
||||
label="Concurrency:{}".format(concur))
|
||||
plt.grid()
|
||||
plt.legend(loc=9, prop={'size':8}, bbox_to_anchor=(0.5, -0.1),
|
||||
ncol=concur)
|
||||
plt.savefig(os.path.splitext(fname)[0]+'.png', bbox_inches='tight')
|
||||
print("Generated plot for aggregated throughput for scenario {}".
|
||||
format(title_name[10]))
|
||||
plt.close()
|
||||
|
||||
|
||||
def generate_perinstance_graphs(data, fname):
|
||||
uuidlist = get_uuidlist(data)
|
||||
agentlist = get_agentlist(uuidlist, data)
|
||||
for key in data['scenarios'].iterkeys():
|
||||
time1 = data['scenarios'][key]['execution']['tests'][0]['time']
|
||||
time = range(time1-1)
|
||||
density=(data['scenarios'][key]['deployment']
|
||||
['accommodation'][2]['density'])
|
||||
concur_list=getiter(data['scenarios'][key])
|
||||
title_name = (data['scenarios'][key]['title']).split('/')
|
||||
for agent in agentlist:
|
||||
resultlist=[0]*(time1-1)
|
||||
for concur in concur_list:
|
||||
for uuid in uuidlist:
|
||||
if (data['records'][uuid]['concurrency'] == concur and
|
||||
data['records'][uuid]['agent'] == agent):
|
||||
for index in range(time1-1):
|
||||
if data['records'][uuid]['status'] == "ok":
|
||||
resultlist[index] = ((data['records'][uuid]
|
||||
['samples'][index][1])/math.pow(10,6))
|
||||
plt.xlabel('Time in seconds')
|
||||
plt.ylabel('Throughput in Mbps')
|
||||
plt.title('Throughput for {} \non node \n{}'.format(
|
||||
agent, data['records'][uuid]['node']), loc='left')
|
||||
plt.title(title_name[10],loc='right')
|
||||
plt.plot(time,resultlist, linewidth=1,marker='o',
|
||||
label="Concurrency:{}".format(concur))
|
||||
plt.grid()
|
||||
plt.legend(loc=9, prop={'size':8}, bbox_to_anchor=(0.5, -0.1),
|
||||
ncol=concur )
|
||||
plt.savefig(os.path.splitext(fname)[0]+ '_' + agent + '.png', bbox_inches='tight')
|
||||
print("Generated plot for agent {} in scenario {}".format(
|
||||
agent, title_name[10]))
|
||||
plt.close()
|
||||
def main():
|
||||
filelist=[]
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Processes shaker results into aggregated graphs')
|
||||
parser.add_argument('result_dir',
|
||||
help='Name of the directory in which results are stored'
|
||||
' Example: 20160226-101636')
|
||||
args = parser.parse_args()
|
||||
shakerplot_path = os.path.dirname(os.path.realpath(__file__))
|
||||
results_path = os.path.join(shakerplot_path.replace('graphing',
|
||||
'results'), args.result_dir)
|
||||
if not os.path.isdir(results_path):
|
||||
print "ERROR Directory doesn't exist"
|
||||
exit(1)
|
||||
for root, dirs, files in os.walk(results_path, topdown=False):
|
||||
for name in files:
|
||||
if name.endswith('.json'):
|
||||
filelist.append(os.path.join(root, name))
|
||||
for fname in filelist:
|
||||
with open(fname) as data_file:
|
||||
data = json.load(data_file)
|
||||
generate_aggregated_graphs(data, fname)
|
||||
generate_perinstance_graphs(data, fname)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,5 +1,19 @@
|
||||
from Tools import *
|
||||
# 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.
|
||||
|
||||
from Tools import Tools
|
||||
import os
|
||||
import logging
|
||||
import shutil
|
||||
|
||||
class Connmon:
|
||||
|
||||
|
@ -1,14 +1,27 @@
|
||||
# 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.
|
||||
|
||||
from elasticsearch import Elasticsearch
|
||||
import logging
|
||||
import json
|
||||
import pprint
|
||||
import numpy
|
||||
import datetime
|
||||
|
||||
|
||||
class Elastic:
|
||||
|
||||
"""
|
||||
"""
|
||||
def __init__(self,config,tool="browbeat") :
|
||||
|
||||
def __init__(self, config, tool="browbeat"):
|
||||
self.config = config
|
||||
self.logger = logging.getLogger('browbeat.Elastic')
|
||||
self.es = Elasticsearch([
|
||||
@ -17,11 +30,12 @@ class Elastic:
|
||||
send_get_body_as='POST'
|
||||
)
|
||||
today = datetime.datetime.today()
|
||||
self.index = "{}-{}".format(tool,today.strftime('%Y.%m.%d'))
|
||||
self.index = "{}-{}".format(tool, today.strftime('%Y.%m.%d'))
|
||||
|
||||
"""
|
||||
"""
|
||||
def load_json(self,result):
|
||||
|
||||
def load_json(self, result):
|
||||
json_data = None
|
||||
self.logger.info("Loading JSON")
|
||||
json_data = json.loads(result)
|
||||
@ -29,34 +43,38 @@ class Elastic:
|
||||
|
||||
"""
|
||||
"""
|
||||
def load_json_file(self,result):
|
||||
|
||||
def load_json_file(self, result):
|
||||
json_data = None
|
||||
self.logger.info("Loading JSON file : {}".format(result))
|
||||
try :
|
||||
with open(result) as jdata :
|
||||
try:
|
||||
with open(result) as jdata:
|
||||
json_data = json.load(jdata)
|
||||
except (IOError, OSError) as e:
|
||||
except (IOError, OSError):
|
||||
self.logger.error("Error loading JSON file : {}".format(result))
|
||||
return False
|
||||
return json_data
|
||||
|
||||
"""
|
||||
"""
|
||||
def combine_metadata(self,result):
|
||||
if len(self.config['elasticsearch']['metadata_files']) > 0 :
|
||||
|
||||
def combine_metadata(self, result):
|
||||
if len(self.config['elasticsearch']['metadata_files']) > 0:
|
||||
meta = self.config['elasticsearch']['metadata_files']
|
||||
for _meta in meta:
|
||||
try :
|
||||
with open(_meta['file']) as jdata :
|
||||
try:
|
||||
with open(_meta['file']) as jdata:
|
||||
result[_meta['name']] = json.load(jdata)
|
||||
except (IOError, OSError) as e:
|
||||
self.logger.error("Error loading Metadata file : {}".format(_meta['file']))
|
||||
except (IOError, OSError):
|
||||
self.logger.error(
|
||||
"Error loading Metadata file : {}".format(_meta['file']))
|
||||
return False
|
||||
return result
|
||||
|
||||
"""
|
||||
"""
|
||||
def index_result(self,result,_type='result',_id=None) :
|
||||
|
||||
def index_result(self, result, _type='result', _id=None):
|
||||
return self.es.index(index=self.index,
|
||||
id=_id,
|
||||
body=result,
|
||||
|
@ -1,3 +1,15 @@
|
||||
# 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 logging
|
||||
import subprocess
|
||||
|
||||
@ -15,8 +27,10 @@ class Grafana:
|
||||
self.grafana_url = {}
|
||||
|
||||
def extra_vars(self, from_ts, to_ts, result_dir, test_name):
|
||||
extra_vars = 'grafana_ip={} '.format(self.config['grafana']['grafana_ip'])
|
||||
extra_vars += 'grafana_port={} '.format(self.config['grafana']['grafana_port'])
|
||||
extra_vars = 'grafana_ip={} '.format(
|
||||
self.config['grafana']['grafana_ip'])
|
||||
extra_vars += 'grafana_port={} '.format(
|
||||
self.config['grafana']['grafana_port'])
|
||||
extra_vars += 'from={} '.format(from_ts)
|
||||
extra_vars += 'to={} '.format(to_ts)
|
||||
extra_vars += 'results_dir={}/{} '.format(result_dir, test_name)
|
||||
@ -35,12 +49,19 @@ class Grafana:
|
||||
url = 'http://{}:{}/dashboard/db/'.format(
|
||||
self.grafana_ip, self.grafana_port)
|
||||
for dashboard in self.config['grafana']['dashboards']:
|
||||
self.grafana_url[dashboard]='{}{}?from={}&to={}&var-Cloud={}'.format(
|
||||
url, dashboard, from_ts, to_ts, self.cloud_name)
|
||||
self.grafana_url[dashboard] = '{}{}?from={}&to={}&var-Cloud={}'.format(
|
||||
url,
|
||||
dashboard,
|
||||
from_ts,
|
||||
to_ts,
|
||||
self.cloud_name)
|
||||
|
||||
def print_dashboard_url(self,test_name):
|
||||
def print_dashboard_url(self, test_name):
|
||||
for dashboard in self.grafana_url:
|
||||
self.logger.info('{} - Grafana Dashboard {} URL: {}'.format(test_name, dashboard,
|
||||
self.logger.info(
|
||||
'{} - Grafana Dashboard {} URL: {}'.format(
|
||||
test_name,
|
||||
dashboard,
|
||||
self.grafana_url[dashboard]))
|
||||
|
||||
def log_snapshot_playbook_cmd(self, from_ts, to_ts, result_dir, test_name):
|
||||
@ -56,8 +77,17 @@ class Grafana:
|
||||
if self.config['grafana']['snapshot']['enabled']:
|
||||
extra_vars = self.extra_vars(
|
||||
from_ts, to_ts, result_dir, test_name)
|
||||
subprocess_cmd = ['ansible-playbook', '-i', self.hosts_file, self.playbook, '-e',
|
||||
subprocess_cmd = [
|
||||
'ansible-playbook',
|
||||
'-i',
|
||||
self.hosts_file,
|
||||
self.playbook,
|
||||
'-e',
|
||||
'{}'.format(extra_vars)]
|
||||
snapshot_log = open('{}/snapshot.log'.format(result_dir), 'a+')
|
||||
self.logger.info('Running ansible to create snapshots for: {}'.format(test_name))
|
||||
subprocess.Popen(subprocess_cmd, stdout=snapshot_log, stderr=subprocess.STDOUT)
|
||||
self.logger.info(
|
||||
'Running ansible to create snapshots for: {}'.format(test_name))
|
||||
subprocess.Popen(
|
||||
subprocess_cmd,
|
||||
stdout=snapshot_log,
|
||||
stderr=subprocess.STDOUT)
|
||||
|
@ -1,9 +1,21 @@
|
||||
# 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 json
|
||||
import re
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class Metadata:
|
||||
|
||||
def __init__(self):
|
||||
@ -11,14 +23,13 @@ class Metadata:
|
||||
|
||||
def load_file(self, filename):
|
||||
json_str = None
|
||||
try :
|
||||
try:
|
||||
with open(filename) as data:
|
||||
json_str = data.read()
|
||||
except IOError:
|
||||
print("Machine facts json is missing")
|
||||
exit(1)
|
||||
regex = re.compile(r"}\n{")
|
||||
new_json = re.sub(r"}\n{",r"},\n{",json_str,re.M)
|
||||
new_json = re.sub(r"}\n{", r"},\n{", json_str, re.M)
|
||||
convert = "{ \"machines\": [" + new_json + "] }"
|
||||
sys_data = {}
|
||||
sys_data['system_data'] = json.loads(convert)
|
||||
@ -32,8 +43,10 @@ class Metadata:
|
||||
hardware_dict = {}
|
||||
hardware_dict['label'] = item['inventory_hostname']
|
||||
hardware_dict['kernel'] = item['ansible_kernel']
|
||||
hardware_dict['total_mem'] = item['ansible_memory_mb']['real']['total']
|
||||
hardware_dict['total_logical_cores'] = item['facter_processorcount']
|
||||
hardware_dict['total_mem'] = item[
|
||||
'ansible_memory_mb']['real']['total']
|
||||
hardware_dict['total_logical_cores'] = item[
|
||||
'facter_processorcount']
|
||||
hardware_dict['os_name'] = item['ansible_distribution'] + \
|
||||
item['ansible_distribution_version']
|
||||
hardware_dict['ip'] = item['ansible_default_ipv4']['address']
|
||||
@ -47,7 +60,7 @@ class Metadata:
|
||||
for item in sys_data['system_data']['machines']:
|
||||
if 'environment_setup' not in env_dict:
|
||||
env_dict['environment_setup'] = {}
|
||||
for key,value in item.items():
|
||||
for key, value in item.items():
|
||||
if 'osp' in key:
|
||||
env_dict['environment_setup'][key] = value
|
||||
return env_dict
|
||||
@ -62,7 +75,8 @@ class Metadata:
|
||||
if 'openstack' not in soft_all_dict['software_details']:
|
||||
soft_all_dict['software_details']['openstack'] = {}
|
||||
if 'config' not in soft_all_dict['software_details']['openstack']:
|
||||
soft_all_dict['software_details']['openstack']['config'] = []
|
||||
soft_all_dict['software_details'][
|
||||
'openstack']['config'] = []
|
||||
software_dict = {}
|
||||
software_dict['node_name'] = item['inventory_hostname']
|
||||
for soft in item:
|
||||
@ -73,7 +87,8 @@ class Metadata:
|
||||
software_dict[service_name] = {}
|
||||
if service_name in soft:
|
||||
software_dict[service_name][soft] = item[soft]
|
||||
soft_all_dict['software_details']['openstack']['config'].append(software_dict)
|
||||
soft_all_dict['software_details']['openstack'][
|
||||
'config'].append(software_dict)
|
||||
return soft_all_dict
|
||||
|
||||
def write_metadata_file(self, data, filename):
|
||||
@ -86,11 +101,14 @@ def main():
|
||||
metadata = Metadata()
|
||||
sysdata = metadata.load_file(_filename)
|
||||
env_data = metadata.get_environment_metadata(sysdata)
|
||||
metadata.write_metadata_file(env_data, os.path.join(sys.argv[1], 'environment-metadata.json'))
|
||||
metadata.write_metadata_file(
|
||||
env_data, os.path.join(sys.argv[1], 'environment-metadata.json'))
|
||||
hardware_data = metadata.get_hardware_metadata(sysdata)
|
||||
metadata.write_metadata_file(hardware_data, os.path.join(sys.argv[1], 'hardware-metadata.json'))
|
||||
metadata.write_metadata_file(
|
||||
hardware_data, os.path.join(sys.argv[1], 'hardware-metadata.json'))
|
||||
software_data = metadata.get_software_metadata(sysdata)
|
||||
metadata.write_metadata_file(software_data, os.path.join(sys.argv[1], 'software-metadata.json'))
|
||||
metadata.write_metadata_file(
|
||||
software_data, os.path.join(sys.argv[1], 'software-metadata.json'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
@ -1,3 +1,15 @@
|
||||
# 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.
|
||||
|
||||
from Connmon import Connmon
|
||||
from Grafana import Grafana
|
||||
from Tools import Tools
|
||||
@ -27,9 +39,12 @@ class PerfKit(WorkloadBase):
|
||||
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))
|
||||
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 update_tests(self):
|
||||
self.test_count += 1
|
||||
@ -79,7 +94,8 @@ class PerfKit(WorkloadBase):
|
||||
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 = 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'])
|
||||
@ -91,8 +107,9 @@ class PerfKit(WorkloadBase):
|
||||
try:
|
||||
self.connmon.move_connmon_results(result_dir, test_name)
|
||||
self.connmon.connmon_graphs(result_dir, test_name)
|
||||
except:
|
||||
self.logger.error("Connmon Result data missing, Connmon never started")
|
||||
except Exception:
|
||||
self.logger.error(
|
||||
"Connmon Result data missing, Connmon never started")
|
||||
|
||||
workload = self.__class__.__name__
|
||||
new_test_name = test_name.split('-')
|
||||
@ -106,17 +123,20 @@ class PerfKit(WorkloadBase):
|
||||
self.update_pass_tests()
|
||||
self.update_total_pass_tests()
|
||||
self.get_time_dict(
|
||||
to_ts, from_ts, benchmark_config['benchmarks'], new_test_name,
|
||||
to_ts, from_ts, benchmark_config[
|
||||
'benchmarks'], new_test_name,
|
||||
workload, "pass")
|
||||
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,
|
||||
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))
|
||||
self.logger.error(
|
||||
"File missing: {}/pkb.stderr.log".format(result_dir))
|
||||
|
||||
# Copy all results
|
||||
for perfkit_file in glob.glob("/tmp/perfkitbenchmarker/run_browbeat/*"):
|
||||
@ -129,7 +149,8 @@ class PerfKit(WorkloadBase):
|
||||
{'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.log_snapshot_playbook_cmd(
|
||||
from_ts, to_ts, result_dir, test_name)
|
||||
self.grafana.run_playbook(from_ts, to_ts, result_dir, test_name)
|
||||
|
||||
def start_workloads(self):
|
||||
@ -148,7 +169,8 @@ class PerfKit(WorkloadBase):
|
||||
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)
|
||||
test_name = "{}-{}-{}".format(time_stamp,
|
||||
benchmark['name'], run)
|
||||
workload = self.__class__.__name__
|
||||
self.workload_logger(result_dir, workload)
|
||||
self.run_benchmark(benchmark, result_dir, test_name)
|
||||
|
97
lib/Rally.py
97
lib/Rally.py
@ -1,20 +1,30 @@
|
||||
# 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.
|
||||
|
||||
from Connmon import Connmon
|
||||
from Tools import Tools
|
||||
from collections import OrderedDict
|
||||
from Grafana import Grafana
|
||||
from WorkloadBase import WorkloadBase
|
||||
from Elastic import Elastic
|
||||
import pprint
|
||||
import numpy
|
||||
import datetime
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import re
|
||||
|
||||
|
||||
class Rally(WorkloadBase):
|
||||
|
||||
def __init__(self, config, hosts=None):
|
||||
@ -52,16 +62,17 @@ class Rally(WorkloadBase):
|
||||
plugin_string = "--plugin-paths {}".format(",".join(plugins))
|
||||
cmd = "source {}; ".format(self.config['rally']['venv'])
|
||||
cmd += "rally {} task start {} --task-args \'{}\' 2>&1 | tee {}.log".format(
|
||||
plugin_string, task_file,task_args, test_name)
|
||||
plugin_string, task_file, task_args, test_name)
|
||||
from_time = time.time()
|
||||
self.tools.run_cmd(cmd)
|
||||
to_time = time.time()
|
||||
if 'sleep_after' in self.config['rally']:
|
||||
time.sleep(self.config['rally']['sleep_after'])
|
||||
to_ts = int(time.time() * 1000)
|
||||
self.grafana.create_grafana_urls({'from_ts':from_ts, 'to_ts':to_ts})
|
||||
self.grafana.create_grafana_urls({'from_ts': from_ts, 'to_ts': to_ts})
|
||||
self.grafana.print_dashboard_url(test_name)
|
||||
self.grafana.log_snapshot_playbook_cmd(from_ts, to_ts, result_dir, 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 (from_time, to_time)
|
||||
|
||||
@ -86,9 +97,12 @@ class Rally(WorkloadBase):
|
||||
self.logger.info(
|
||||
"Current number of Rally scenarios executed:{}".format(
|
||||
self.scenario_count))
|
||||
self.logger.info("Current number of Rally tests executed:{}".format(self.test_count))
|
||||
self.logger.info("Current number of Rally tests passed:{}".format(self.pass_count))
|
||||
self.logger.info("Current number of Rally test failures:{}".format(self.error_count))
|
||||
self.logger.info(
|
||||
"Current number of Rally tests executed:{}".format(self.test_count))
|
||||
self.logger.info(
|
||||
"Current number of Rally tests passed:{}".format(self.pass_count))
|
||||
self.logger.info(
|
||||
"Current number of Rally test failures:{}".format(self.error_count))
|
||||
|
||||
def gen_scenario_html(self, task_ids, test_name):
|
||||
all_task_ids = ' '.join(task_ids)
|
||||
@ -107,57 +121,48 @@ class Rally(WorkloadBase):
|
||||
cmd += "rally task results {} > {}.json".format(task_id, test_name)
|
||||
return self.tools.run_cmd(cmd)
|
||||
|
||||
def rally_metadata(self, result, meta) :
|
||||
def rally_metadata(self, result, meta):
|
||||
result['rally_metadata'] = meta
|
||||
return result
|
||||
|
||||
def json_result(self,task_id,scenario_name):
|
||||
def json_result(self, task_id, scenario_name):
|
||||
rally_data = {}
|
||||
rally_errors = []
|
||||
rally_sla = []
|
||||
self.logger.info("Loadding Task_ID {} JSON".format(task_id))
|
||||
rally_json = self.elastic.load_json(self.gen_scenario_json(task_id))
|
||||
es_ts = datetime.datetime.utcnow()
|
||||
if len(rally_json) < 1 :
|
||||
if len(rally_json) < 1:
|
||||
self.logger.error("Issue with Rally Results")
|
||||
return False
|
||||
for metrics in rally_json[0]['result']:
|
||||
for workload in metrics :
|
||||
for workload in metrics:
|
||||
if type(metrics[workload]) is dict:
|
||||
for value in metrics[workload] :
|
||||
for value in metrics[workload]:
|
||||
if not type(metrics[workload][value]) is list:
|
||||
if value not in rally_data:
|
||||
rally_data[value] = []
|
||||
rally_data[value].append(metrics[workload][value])
|
||||
if len(metrics['error']) > 0 :
|
||||
if len(metrics['error']) > 0:
|
||||
error = {'action_name': value,
|
||||
'error': metrics['error'],
|
||||
'result': task_id,
|
||||
'timestamp': es_ts,
|
||||
'scenario' : scenario_name,
|
||||
'scenario': scenario_name,
|
||||
}
|
||||
self.elastic.index_result(error,'config')
|
||||
rally_doc = []
|
||||
self.elastic.index_result(error, 'config')
|
||||
for workload in rally_data:
|
||||
if not type(rally_data[workload]) is dict :
|
||||
if not type(rally_data[workload]) is dict:
|
||||
iteration = 1
|
||||
workload_name = workload
|
||||
if workload.find('(') is not -1 :
|
||||
iteration = re.findall('\d+',workload)
|
||||
if workload.find('(') is not -1:
|
||||
iteration = re.findall('\d+', workload)
|
||||
workload_name = workload.split('(')[0]
|
||||
rally_stats = {'result': task_id,
|
||||
'action': workload_name,
|
||||
'iteration': iteration,
|
||||
#'90th':numpy.percentile(rally_data[workload], 90),
|
||||
#'95th':numpy.percentile(rally_data[workload], 95),
|
||||
#'max':numpy.max(rally_data[workload]),
|
||||
#'min':numpy.min(rally_data[workload]),
|
||||
#'average':numpy.average(rally_data[workload]),
|
||||
#'median':numpy.median(rally_data[workload]),
|
||||
'timestamp': es_ts,
|
||||
'scenario' : scenario_name,
|
||||
'scenario': scenario_name,
|
||||
'rally_setup': rally_json[0]['key'],
|
||||
'raw':rally_data[workload]}
|
||||
'raw': rally_data[workload]}
|
||||
result = self.elastic.combine_metadata(rally_stats)
|
||||
self.elastic.index_result(result)
|
||||
return True
|
||||
@ -202,7 +207,8 @@ class Rally(WorkloadBase):
|
||||
self.config['browbeat'][
|
||||
'results'], dir_ts, benchmark['name'],
|
||||
scenario_name)
|
||||
self.logger.debug("Created result directory: {}".format(result_dir))
|
||||
self.logger.debug(
|
||||
"Created result directory: {}".format(result_dir))
|
||||
workload = self.__class__.__name__
|
||||
self.workload_logger(result_dir, workload)
|
||||
|
||||
@ -234,7 +240,7 @@ class Rally(WorkloadBase):
|
||||
if self.config['connmon']['enabled']:
|
||||
self.connmon.start_connmon()
|
||||
|
||||
from_time,to_time = self.run_scenario(
|
||||
from_time, to_time = self.run_scenario(
|
||||
scenario_file, scenario, result_dir, test_name,
|
||||
benchmark['name'])
|
||||
|
||||
@ -244,12 +250,13 @@ class Rally(WorkloadBase):
|
||||
try:
|
||||
self.connmon.move_connmon_results(
|
||||
result_dir, test_name)
|
||||
except:
|
||||
except Exception:
|
||||
self.logger.error(
|
||||
"Connmon Result data missing, \
|
||||
Connmon never started")
|
||||
return False
|
||||
self.connmon.connmon_graphs(result_dir, test_name)
|
||||
self.connmon.connmon_graphs(
|
||||
result_dir, test_name)
|
||||
new_test_name = test_name.split('-')
|
||||
new_test_name = new_test_name[3:]
|
||||
new_test_name = "-".join(new_test_name)
|
||||
@ -261,23 +268,29 @@ class Rally(WorkloadBase):
|
||||
self.logger.info(
|
||||
"Generating Rally HTML for task_id : {}".
|
||||
format(task_id))
|
||||
self.gen_scenario_html([task_id], test_name)
|
||||
self.gen_scenario_json_file(task_id, test_name)
|
||||
self.gen_scenario_html(
|
||||
[task_id], test_name)
|
||||
self.gen_scenario_json_file(
|
||||
task_id, test_name)
|
||||
results[run].append(task_id)
|
||||
self.update_pass_tests()
|
||||
self.update_total_pass_tests()
|
||||
self.get_time_dict(
|
||||
to_time, from_time, benchmark['name'], new_test_name,
|
||||
to_time, from_time, benchmark[
|
||||
'name'], new_test_name,
|
||||
workload, "pass")
|
||||
if self.config['elasticsearch']['enabled'] :
|
||||
if self.config['elasticsearch']['enabled']:
|
||||
# Start indexing
|
||||
result_json = self.json_result(task_id,scenario_name)
|
||||
self.json_result(
|
||||
task_id, scenario_name)
|
||||
else:
|
||||
self.logger.error("Cannot find task_id")
|
||||
self.logger.error(
|
||||
"Cannot find task_id")
|
||||
self.update_fail_tests()
|
||||
self.update_total_fail_tests()
|
||||
self.get_time_dict(
|
||||
to_time, from_time, benchmark['name'], new_test_name,
|
||||
to_time, from_time, benchmark[
|
||||
'name'], new_test_name,
|
||||
workload, "fail")
|
||||
|
||||
for data in glob.glob("./{}*".format(test_name)):
|
||||
|
@ -1,3 +1,15 @@
|
||||
# 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.
|
||||
|
||||
from Tools import Tools
|
||||
from Grafana import Grafana
|
||||
from WorkloadBase import WorkloadBase
|
||||
@ -8,6 +20,7 @@ import os
|
||||
import json
|
||||
import time
|
||||
|
||||
|
||||
class Shaker(WorkloadBase):
|
||||
|
||||
def __init__(self, config):
|
||||
@ -29,15 +42,22 @@ class Shaker(WorkloadBase):
|
||||
self.logger.info("Shaker image is built, continuing")
|
||||
|
||||
def get_stats(self):
|
||||
self.logger.info("Current number of Shaker tests executed: {}".format(self.test_count))
|
||||
self.logger.info("Current number of Shaker tests passed: {}".format(self.pass_count))
|
||||
self.logger.info("Current number of Shaker tests failed: {}".format(self.error_count))
|
||||
self.logger.info(
|
||||
"Current number of Shaker tests executed: {}".format(self.test_count))
|
||||
self.logger.info(
|
||||
"Current number of Shaker tests passed: {}".format(self.pass_count))
|
||||
self.logger.info(
|
||||
"Current number of Shaker tests failed: {}".format(self.error_count))
|
||||
|
||||
def final_stats(self, total):
|
||||
self.logger.info("Total Shaker scenarios enabled by user: {}".format(total))
|
||||
self.logger.info("Total number of Shaker tests executed: {}".format(self.test_count))
|
||||
self.logger.info("Total number of Shaker tests passed: {}".format(self.pass_count))
|
||||
self.logger.info("Total number of Shaker tests failed: {}".format(self.error_count))
|
||||
self.logger.info(
|
||||
"Total Shaker scenarios enabled by user: {}".format(total))
|
||||
self.logger.info(
|
||||
"Total number of Shaker tests executed: {}".format(self.test_count))
|
||||
self.logger.info(
|
||||
"Total number of Shaker tests passed: {}".format(self.pass_count))
|
||||
self.logger.info(
|
||||
"Total number of Shaker tests failed: {}".format(self.error_count))
|
||||
|
||||
def update_tests(self):
|
||||
self.test_count += 1
|
||||
@ -96,7 +116,7 @@ class Shaker(WorkloadBase):
|
||||
return uuidlist
|
||||
|
||||
def result_check(self, result_dir, test_name, scenario, to_time, from_time):
|
||||
outputfile = os.path.join(result_dir,test_name + "." + "json")
|
||||
outputfile = os.path.join(result_dir, test_name + "." + "json")
|
||||
error = False
|
||||
with open(outputfile) as data_file:
|
||||
data = json.load(data_file)
|
||||
@ -110,7 +130,8 @@ class Shaker(WorkloadBase):
|
||||
error = True
|
||||
if error:
|
||||
self.logger.error("Failed Test: {}".format(scenario['name']))
|
||||
self.logger.error("saved log to: {}.log".format(os.path.join(result_dir, test_name)))
|
||||
self.logger.error(
|
||||
"saved log to: {}.log".format(os.path.join(result_dir, test_name)))
|
||||
self.update_fail_tests()
|
||||
self.update_total_fail_tests()
|
||||
self.get_time_dict(
|
||||
@ -129,7 +150,8 @@ class Shaker(WorkloadBase):
|
||||
test_name +
|
||||
"." +
|
||||
"html")))
|
||||
self.logger.info("saved log to: {}.log".format(os.path.join(result_dir, test_name)))
|
||||
self.logger.info(
|
||||
"saved log to: {}.log".format(os.path.join(result_dir, test_name)))
|
||||
self.update_pass_tests()
|
||||
self.update_total_pass_tests()
|
||||
self.get_time_dict(
|
||||
@ -199,11 +221,13 @@ class Shaker(WorkloadBase):
|
||||
self.logger.debug("Set Scenario File: {}".format(
|
||||
scenario['file']))
|
||||
result_dir = self.tools.create_results_dir(
|
||||
self.config['browbeat']['results'], time_stamp, "shaker",
|
||||
self.config['browbeat'][
|
||||
'results'], time_stamp, "shaker",
|
||||
scenario['name'])
|
||||
workload = self.__class__.__name__
|
||||
self.workload_logger(result_dir, workload)
|
||||
time_stamp1 = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||
time_stamp1 = datetime.datetime.now().strftime(
|
||||
"%Y%m%d-%H%M%S")
|
||||
test_name = "{}-browbeat-{}-{}".format(time_stamp1,
|
||||
"shaker", scenario['name'])
|
||||
self.run_scenario(scenario, result_dir, test_name)
|
||||
|
20
lib/Tools.py
20
lib/Tools.py
@ -1,7 +1,19 @@
|
||||
# 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 logging
|
||||
import os
|
||||
import shutil
|
||||
from subprocess import Popen, PIPE
|
||||
from subprocess import Popen
|
||||
from subprocess import PIPE
|
||||
|
||||
|
||||
class Tools:
|
||||
@ -37,7 +49,7 @@ class Tools:
|
||||
try:
|
||||
os.makedirs("%s/run-%s" % (results_dir, run))
|
||||
return "%s/run-%s" % (results_dir, run)
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
# Create directory for results
|
||||
@ -48,5 +60,5 @@ class Tools:
|
||||
self.logger.debug("{}/{}/{}/{}".format(os.path.dirname(results_dir), timestamp, service,
|
||||
scenario))
|
||||
return "{}/{}/{}/{}".format(os.path.dirname(results_dir), timestamp, service, scenario)
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
return False
|
||||
|
@ -1,8 +1,22 @@
|
||||
from abc import ABCMeta, abstractmethod
|
||||
# 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.
|
||||
|
||||
from abc import ABCMeta
|
||||
from abc import abstractmethod
|
||||
import os
|
||||
import logging
|
||||
import yaml
|
||||
import collections
|
||||
|
||||
|
||||
class WorkloadBase:
|
||||
__metaclass__ = ABCMeta
|
||||
success = 0
|
||||
@ -45,7 +59,8 @@ class WorkloadBase:
|
||||
file = logging.FileHandler(
|
||||
"{}/{}/browbeat-{}-run.log".format(base[0], base[1], workload))
|
||||
file.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)5s - %(message)s')
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)5s - %(message)s')
|
||||
file.setFormatter(formatter)
|
||||
self.logger.addHandler(file)
|
||||
return None
|
||||
@ -63,16 +78,18 @@ class WorkloadBase:
|
||||
|
||||
@staticmethod
|
||||
def print_report(result_dir, time_stamp):
|
||||
with open(os.path.join(result_dir,time_stamp + '.' + 'report'), 'w') as yaml_file:
|
||||
with open(os.path.join(result_dir, time_stamp + '.' + 'report'), 'w') as yaml_file:
|
||||
yaml_file.write("Browbeat Report Card\n")
|
||||
if not WorkloadBase.browbeat:
|
||||
yaml_file.write("No tests were enabled")
|
||||
else:
|
||||
yaml_file.write(yaml.dump(WorkloadBase.browbeat, default_flow_style=False))
|
||||
yaml_file.write(
|
||||
yaml.dump(WorkloadBase.browbeat, default_flow_style=False))
|
||||
|
||||
@staticmethod
|
||||
def print_summary():
|
||||
print("Total scenarios executed:{}".format(WorkloadBase.total_scenarios))
|
||||
print("Total scenarios executed:{}".format(
|
||||
WorkloadBase.total_scenarios))
|
||||
print("Total tests executed:{}".format(WorkloadBase.total_tests))
|
||||
print("Total tests passed:{}".format(WorkloadBase.success))
|
||||
print("Total tests failed:{}".format(WorkloadBase.failure))
|
||||
|
@ -1,233 +1,372 @@
|
||||
name: Browbeat configuration schema
|
||||
type: map
|
||||
allowempty: True
|
||||
name:
|
||||
Browbeat configuration schema
|
||||
type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
browbeat:
|
||||
required: True
|
||||
type: map
|
||||
required:
|
||||
True
|
||||
type:
|
||||
map
|
||||
mapping:
|
||||
results:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
rerun:
|
||||
type: int
|
||||
required: True
|
||||
type:
|
||||
int
|
||||
required:
|
||||
True
|
||||
|
||||
ansible:
|
||||
required: True
|
||||
type: map
|
||||
allowempty: True
|
||||
required:
|
||||
True
|
||||
type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
hosts:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
adjust:
|
||||
type: map
|
||||
type:
|
||||
map
|
||||
mapping:
|
||||
keystone_token:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
neutron_l3:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
nova_db:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
workers:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
grafana_snapshot:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
shaker_build:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
|
||||
connmon:
|
||||
type: map
|
||||
allowempty: True
|
||||
type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
|
||||
grafana:
|
||||
required: True
|
||||
type: map
|
||||
allowempty: True
|
||||
required:
|
||||
True
|
||||
type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
cloud_name:
|
||||
type: str
|
||||
type:
|
||||
str
|
||||
grafana_ip:
|
||||
type: str
|
||||
pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$
|
||||
type:
|
||||
str
|
||||
pattern:
|
||||
^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\- ]{0, 61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\- ]{0, 61}[a-zA-Z0-9]))*$
|
||||
grafana_port:
|
||||
type: int
|
||||
type:
|
||||
int
|
||||
dashboards:
|
||||
type: seq
|
||||
type:
|
||||
seq
|
||||
sequence:
|
||||
- type: str
|
||||
- type:
|
||||
str
|
||||
snapshot:
|
||||
type: map
|
||||
type:
|
||||
map
|
||||
mapping:
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
snapshot_compute:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
|
||||
perfkit:
|
||||
required: False
|
||||
type: map
|
||||
allowempty: True
|
||||
required:
|
||||
False
|
||||
type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
sleep_before:
|
||||
type: number
|
||||
required: True
|
||||
type:
|
||||
number
|
||||
required:
|
||||
True
|
||||
sleep_after:
|
||||
type: number
|
||||
required: True
|
||||
type:
|
||||
number
|
||||
required:
|
||||
True
|
||||
venv:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
default:
|
||||
type: map
|
||||
required: True
|
||||
type:
|
||||
map
|
||||
required:
|
||||
True
|
||||
mapping:
|
||||
image:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
machine_type:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
os_type:
|
||||
type: str
|
||||
required: True
|
||||
enum: ['rhel', 'debian', 'ubuntu_container', 'windows']
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
enum:
|
||||
['rhel', 'debian', 'ubuntu_container', 'windows']
|
||||
openstack_image_username:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
openstack_floating_ip_pool:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
openstack_network:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
benchmarks:
|
||||
type: seq
|
||||
type:
|
||||
seq
|
||||
sequence:
|
||||
- type: map
|
||||
allowempty: True
|
||||
- type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
name:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
benchmarks:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
|
||||
shaker:
|
||||
required: False
|
||||
allowempty: True
|
||||
type: map
|
||||
required:
|
||||
False
|
||||
allowempty:
|
||||
True
|
||||
type:
|
||||
map
|
||||
mapping:
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
server:
|
||||
type: str
|
||||
required: True
|
||||
pattern: ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
pattern:
|
||||
^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0, 61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\- ]{0, 61}[a-zA-Z0-9]))*$
|
||||
port:
|
||||
type: int
|
||||
required: True
|
||||
type:
|
||||
int
|
||||
required:
|
||||
True
|
||||
flavor:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
join_timeout:
|
||||
type: int
|
||||
required: True
|
||||
type:
|
||||
int
|
||||
required:
|
||||
True
|
||||
sleep_before:
|
||||
type: number
|
||||
required: True
|
||||
type:
|
||||
number
|
||||
required:
|
||||
True
|
||||
sleep_after:
|
||||
type: number
|
||||
required: True
|
||||
type:
|
||||
number
|
||||
required:
|
||||
True
|
||||
venv:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
shaker_region:
|
||||
type: str
|
||||
required: true
|
||||
type:
|
||||
str
|
||||
required:
|
||||
true
|
||||
scenarios:
|
||||
type: seq
|
||||
type:
|
||||
seq
|
||||
sequence:
|
||||
- type: map
|
||||
allowempty: True
|
||||
- type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
name:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
file:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
|
||||
rally:
|
||||
required: False
|
||||
type: map
|
||||
allowempty: True
|
||||
required:
|
||||
False
|
||||
type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
sleep_before:
|
||||
type: number
|
||||
required: True
|
||||
type:
|
||||
number
|
||||
required:
|
||||
True
|
||||
sleep_after:
|
||||
type: number
|
||||
required: True
|
||||
type:
|
||||
number
|
||||
required:
|
||||
True
|
||||
venv:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
benchmarks:
|
||||
type: seq
|
||||
required: True
|
||||
type:
|
||||
seq
|
||||
required:
|
||||
True
|
||||
sequence:
|
||||
- type: map
|
||||
- type:
|
||||
map
|
||||
mapping:
|
||||
name:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
enabled:
|
||||
required: True
|
||||
type: bool
|
||||
required:
|
||||
True
|
||||
type:
|
||||
bool
|
||||
concurrency:
|
||||
type: seq
|
||||
required: True
|
||||
type:
|
||||
seq
|
||||
required:
|
||||
True
|
||||
sequence:
|
||||
- type: int
|
||||
- type:
|
||||
int
|
||||
times:
|
||||
type: int
|
||||
required: True
|
||||
type:
|
||||
int
|
||||
required:
|
||||
True
|
||||
scenarios:
|
||||
type: seq
|
||||
type:
|
||||
seq
|
||||
sequence:
|
||||
- type: map
|
||||
allowempty: True
|
||||
- type:
|
||||
map
|
||||
allowempty:
|
||||
True
|
||||
mapping:
|
||||
name:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
enabled:
|
||||
type: bool
|
||||
required: True
|
||||
type:
|
||||
bool
|
||||
required:
|
||||
True
|
||||
file:
|
||||
type: str
|
||||
required: True
|
||||
type:
|
||||
str
|
||||
required:
|
||||
True
|
||||
|
@ -1,12 +1,23 @@
|
||||
# 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.
|
||||
|
||||
from rally.task import atomic
|
||||
from rally.task import scenario
|
||||
from rally.plugins.openstack.scenarios.nova import utils as nova_utils
|
||||
from rally.plugins.openstack.scenarios.neutron import utils as neutron_utils
|
||||
from rally.plugins.openstack.scenarios.vm import utils as vm_utils
|
||||
from rally.task import types
|
||||
from rally.task import utils as task_utils
|
||||
from rally.task import validation
|
||||
|
||||
|
||||
class NeutronBootFipPingPlugin(neutron_utils.NeutronScenario,
|
||||
vm_utils.VMScenario,
|
||||
scenario.Scenario):
|
||||
@ -19,36 +30,35 @@ class NeutronBootFipPingPlugin(neutron_utils.NeutronScenario,
|
||||
# Ping
|
||||
# Cleanup
|
||||
#
|
||||
|
||||
@types.set(image=types.ImageResourceType,
|
||||
flavor=types.FlavorResourceType)
|
||||
@validation.image_valid_on_flavor("flavor", "image")
|
||||
@validation.required_openstack(users=True)
|
||||
@scenario.configure(context={"cleanup": ["nova", "neutron"],
|
||||
"keypair": {}, "allow_ssh": {}})
|
||||
def create_network_nova_boot_ping(self,image,flavor,ext_net,floating=False,router=None,
|
||||
network_create_args=None,subnet_create_args=None,
|
||||
def create_network_nova_boot_ping(self, image, flavor, ext_net, floating=False, router=None,
|
||||
network_create_args=None, subnet_create_args=None,
|
||||
**kwargs):
|
||||
if router == None:
|
||||
router = self._create_router({},ext_net)
|
||||
if router is None:
|
||||
router = self._create_router({}, ext_net)
|
||||
|
||||
network = self._create_network(network_create_args or {})
|
||||
subnet = self._create_subnet(network, subnet_create_args or {})
|
||||
self._add_interface_router(subnet['subnet'],router['router'])
|
||||
kwargs["nics"] = [{ 'net-id': network['network']['id']}]
|
||||
self._add_interface_router(subnet['subnet'], router['router'])
|
||||
kwargs["nics"] = [{'net-id': network['network']['id']}]
|
||||
_address = None
|
||||
if floating :
|
||||
_guest = self._boot_server_with_fip(image, flavor,True,ext_net, **kwargs)
|
||||
if floating:
|
||||
_guest = self._boot_server_with_fip(
|
||||
image, flavor, True, ext_net, **kwargs)
|
||||
_address = _guest[1]['ip']
|
||||
else:
|
||||
self._boot_server(image, flavor,**kwargs)
|
||||
self._boot_server(image, flavor, **kwargs)
|
||||
_address = ""
|
||||
|
||||
if _address:
|
||||
self._wait_for_ping(_address)
|
||||
|
||||
|
||||
|
||||
|
||||
@atomic.action_timer("neutronPlugin.create_router")
|
||||
def _create_router(self, router_create_args, external_gw=False):
|
||||
"""Create neutron router.
|
||||
@ -80,4 +90,3 @@ class NeutronBootFipPingPlugin(neutron_utils.NeutronScenario,
|
||||
|
||||
return self.clients("neutron").create_router(
|
||||
{"router": router_create_args})
|
||||
|
||||
|
@ -1,29 +1,41 @@
|
||||
from rally.task import atomic
|
||||
# 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.
|
||||
|
||||
from rally.task import scenario
|
||||
from rally.plugins.openstack.scenarios.nova import utils as nova_utils
|
||||
from rally.plugins.openstack.scenarios.neutron import utils as neutron_utils
|
||||
from rally.task import types
|
||||
from rally.task import utils as task_utils
|
||||
from rally.task import validation
|
||||
|
||||
|
||||
class NeutronPlugin(neutron_utils.NeutronScenario,
|
||||
nova_utils.NovaScenario,
|
||||
scenario.Scenario):
|
||||
|
||||
@types.set(image=types.ImageResourceType,
|
||||
flavor=types.FlavorResourceType)
|
||||
@validation.image_valid_on_flavor("flavor", "image")
|
||||
@validation.required_openstack(users=True)
|
||||
@scenario.configure(context={"cleanup": ["nova","neutron"]})
|
||||
def create_network_nova_boot(self,image,flavor,num_networks=1,network_create_args=None,
|
||||
subnet_create_args=None,**kwargs):
|
||||
nets=[]
|
||||
for net in range(1,num_networks):
|
||||
@scenario.configure(context={"cleanup": ["nova", "neutron"]})
|
||||
def create_network_nova_boot(self, image, flavor, num_networks=1, network_create_args=None,
|
||||
subnet_create_args=None, **kwargs):
|
||||
nets = []
|
||||
for net in range(1, num_networks):
|
||||
network = self._create_network(network_create_args or {})
|
||||
subnet = self._create_subnet(network, subnet_create_args or {})
|
||||
self._create_subnet(network, subnet_create_args or {})
|
||||
nets.append(network)
|
||||
|
||||
kwargs["nics"] = []
|
||||
for net in nets:
|
||||
kwargs["nics"].append({'net-id':net['network']['id']})
|
||||
kwargs["nics"].append({'net-id': net['network']['id']})
|
||||
|
||||
self._boot_server(image, flavor, **kwargs)
|
||||
|
@ -1,9 +1,18 @@
|
||||
from rally.task import atomic
|
||||
# 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.
|
||||
|
||||
from rally.task import scenario
|
||||
from rally.plugins.openstack.scenarios.nova import utils as nova_utils
|
||||
from rally.plugins.openstack.scenarios.neutron import utils as neutron_utils
|
||||
from rally.task import types
|
||||
from rally.task import utils as task_utils
|
||||
from rally.task import validation
|
||||
|
||||
class NeutronPlugin(neutron_utils.NeutronScenario,
|
||||
|
@ -1,5 +1,4 @@
|
||||
ansible
|
||||
matplotlib
|
||||
python-dateutil==2.4.2
|
||||
pykwalify
|
||||
elasticsearch
|
||||
|
47
setup.cfg
47
setup.cfg
@ -1,3 +1,44 @@
|
||||
[pep8]
|
||||
ignore = E226,E302,E41,E111,E231,E203
|
||||
max-line-length = 100
|
||||
[metadata]
|
||||
name = browbeat
|
||||
summary = OpenStack Performance Tooling
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://www.browbeatproject.org/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.3
|
||||
Programming Language :: Python :: 3.4
|
||||
|
||||
#[files]
|
||||
#packages =
|
||||
# browbeat
|
||||
|
||||
#[build_sphinx]
|
||||
#source-dir = doc/source
|
||||
#build-dir = doc/build
|
||||
#all_files = 1
|
||||
|
||||
#[upload_sphinx]
|
||||
#upload-dir = doc/build/html
|
||||
|
||||
#[compile_catalog]
|
||||
#directory = browbeat/locale
|
||||
#domain = browbeat
|
||||
|
||||
#[update_catalog]
|
||||
#domain = browbeat
|
||||
#output_dir = browbeat/locale
|
||||
#input_file = browbeat/locale/browbeat.pot
|
||||
|
||||
#[extract_messages]
|
||||
#keywords = _ gettext ngettext l_ lazy_gettext
|
||||
#mapping_file = babel.cfg
|
||||
#output_file = browbeat/locale/browbeat.pot
|
||||
|
17
setup.py
Normal file
17
setup.py
Normal file
@ -0,0 +1,17 @@
|
||||
# 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 setuptools
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
14
test-requirements.txt
Normal file
14
test-requirements.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
hacking<0.11,>=0.10.0
|
||||
|
||||
coverage>=3.6
|
||||
python-subunit>=0.0.18
|
||||
sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
|
||||
oslosphinx>=2.5.0 # Apache-2.0
|
||||
oslotest>=1.10.0 # Apache-2.0
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=1.4.0
|
62
tox.ini
Normal file
62
tox.ini
Normal file
@ -0,0 +1,62 @@
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
#envlist = py34-constraints,py27-constraints,pypy-constraints,pep8-constraints
|
||||
envlist = py27-constraints,pypy-constraints,pep8-constraints
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command =
|
||||
constraints: {[testenv:common-constraints]install_command}
|
||||
pip install -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py test
|
||||
#commands = python setup.py test --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:common-constraints]
|
||||
install_command = pip install -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 {posargs} --exclude=ansible
|
||||
|
||||
[testenv:pep8-constraints]
|
||||
install_command = {[testenv:common-constraints]install_command}
|
||||
commands = flake8 {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:venv-constraints]
|
||||
install_command = {[testenv:common-constraints]install_command}
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:cover-constraints]
|
||||
install_command = {[testenv:common-constraints]install_command}
|
||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:docs-constraints]
|
||||
install_command = {[testenv:common-constraints]install_command}
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[testenv:debug-constraints]
|
||||
install_command = {[testenv:common-constraints]install_command}
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
show-source = True
|
||||
ignore = E123,E125,E226,E302,E41,E111,E231,E203,H233,H306,H238,H236,H404,H405
|
||||
max-line-length = 100
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,ansible/*
|
Loading…
Reference in New Issue
Block a user