Added I/O Benchmark PNG graph sections

This commit is contained in:
2Shirt 2020-01-22 19:19:54 -07:00
parent 8643ec2c7c
commit 701d647a91
Signed by: 2Shirt
GPG key ID: 152FAC923B0E132C
3 changed files with 136 additions and 3 deletions

View file

@ -28,6 +28,8 @@ CRASH_SERVER = {
'Pass': '', 'Pass': '',
'Headers': {'X-Requested-With': 'XMLHttpRequest'}, 'Headers': {'X-Requested-With': 'XMLHttpRequest'},
} }
# Misc
IMGUR_CLIENT_ID='3d1ee1d38707b85'
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -2,11 +2,24 @@
# pylint: disable=bad-whitespace # pylint: disable=bad-whitespace
# vim: sts=2 sw=2 ts=2 # vim: sts=2 sw=2 ts=2
import base64
import json
import logging import logging
import math
import pathlib
import time
import Gnuplot
import requests
from wk.cfg.net import BENCHMARK_SERVER, IMGUR_CLIENT_ID
from wk.std import color_string from wk.std import color_string
# Hack to hide X11 error when running in CLI mode
Gnuplot.gp.GnuplotOpts.default_term = 'xterm'
# STATIC VARIABLES # STATIC VARIABLES
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
GRAPH_HORIZONTAL = ('', '', '', '', '', '', '', '') GRAPH_HORIZONTAL = ('', '', '', '', '', '', '', '')
@ -34,6 +47,48 @@ THRESH_GREAT = 750 * 1024**2
# Functions # Functions
def export_io_graph(disk, log_dir):
"""Exports PNG graph using gnuplot, returns pathlib.Path obj."""
read_rates = disk.tests['Disk I/O Benchmark'].read_rates
# Safety check
if not read_rates:
raise RuntimeError(f'No read rates for {disk.path}')
# Prep
max_rate = max(read_rates) / (1024**2)
max_rate = max(800, max_rate)
out_path = pathlib.Path(f'{log_dir}/{disk.path.name}_iobenchmark.png')
plot_data = out_path.with_suffix('.data')
# Adjust Y-axis range to either 1000 or roughly max rate + 200
## Round up to the nearest 100 and then add 200
y_range = (math.ceil(max_rate/100)*100) + 200
# Save plot data to file for Gnuplot
with open(plot_data, 'w') as _f:
for i, rate in enumerate(read_rates):
percent = (i+1) / len(read_rates) * 100
rate = int(rate / (1024**2))
_f.write(f'{percent:0.1f} {rate}\n')
# Run gnuplot commands
_g = Gnuplot.Gnuplot()
_g('reset')
_g(f'set output "{out_path}"')
_g('set terminal png large size 660,300 truecolor font "Noto Sans,11"')
_g('set title "I/O Benchmark"')
_g(f'set yrange [0:{y_range}]')
_g('set style data lines')
_g(f'plot "{plot_data}" title "{disk.description.replace("_", " ")}"')
# Cleanup
_g.close()
del _g
return out_path
def generate_horizontal_graph(rate_list, graph_width=40, oneline=False): def generate_horizontal_graph(rate_list, graph_width=40, oneline=False):
"""Generate horizontal graph from rate_list, returns list.""" """Generate horizontal graph from rate_list, returns list."""
graph = ['', '', '', ''] graph = ['', '', '', '']
@ -111,6 +166,68 @@ def merge_rates(rates, graph_width=40):
return merged_rates return merged_rates
def upload_to_imgur(image_path):
"""Upload image to Imgur and return image url as str."""
image_data = None
image_link = None
# Bail early
if not image_path:
raise RuntimeError(f'Invalid image path: {image_path}')
# Read image file and convert to base64 then convert to str
with open(image_path, 'rb') as _f:
image_data = base64.b64encode(_f.read()).decode()
# POST image
url = 'https://api.imgur.com/3/image'
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
payload = (
f'--{boundary}\r\nContent-Disposition: form-data; '
f'name="image"\r\n\r\n{image_data}\r\n--{boundary}--'
)
headers = {
'content-type': f'multipart/form-data; boundary={boundary}',
'Authorization': f'Client-ID {IMGUR_CLIENT_ID}',
}
response = requests.request('POST', url, data=payload, headers=headers)
# Return image link
if response.ok:
json_data = json.loads(response.text)
image_link = json_data['data']['link']
return image_link
def upload_to_nextcloud(image_path, ticket_number, dev_name):
"""Upload image to Nextcloud server and return folder url as str."""
image_data = None
# Bail early
if not image_path:
raise RuntimeError(f'Invalid image path: {image_path}')
# Read image file and convert to base64
with open(image_path, 'rb') as _f:
image_data = _f.read()
# PUT image
url = (
f'{BENCHMARK_SERVER["Url"]}/'
f'{ticket_number}_iobenchmark'
f'_{dev_name}_{time.strftime("%Y-%m-%d_%H%M_%z")}.png'
)
requests.put(
url,
data=image_data,
headers = {'X-Requested-With': 'XMLHttpRequest'},
auth = (BENCHMARK_SERVER['User'], BENCHMARK_SERVER['Pass']),
)
# Return folder link
return BENCHMARK_SERVER['Short Url']
def vertical_graph_line(percent, rate, scale=32): def vertical_graph_line(percent, rate, scale=32):
"""Build colored graph string using thresholds, returns str.""" """Build colored graph string using thresholds, returns str."""
color_bar = None color_bar = None

View file

@ -748,7 +748,7 @@ def cpu_mprime_test(state, test_objects):
std.print_info('Posting results to osTicket...') std.print_info('Posting results to osTicket...')
test_cooling_obj.cpu_max_temp = sensors.cpu_max_temp() test_cooling_obj.cpu_max_temp = sensors.cpu_max_temp()
state.ost.post_response( state.ost.post_response(
ost_build_report(state.cpu, 'CPU'), ost_build_report(state.cpu, 'CPU', state),
color='Diags FAIL' if state.cpu.any_test_failed() else 'Diags', color='Diags FAIL' if state.cpu.any_test_failed() else 'Diags',
) )
@ -860,6 +860,9 @@ def disk_io_benchmark(state, test_objects, skip_usb=True):
# Check results # Check results
check_io_benchmark_results(test_obj, read_rates, IO_GRAPH_WIDTH) check_io_benchmark_results(test_obj, read_rates, IO_GRAPH_WIDTH)
# osTicket
test_obj.read_rates = read_rates
# Run benchmarks # Run benchmarks
state.update_top_pane( state.update_top_pane(
f'Disk I/O Benchmark{"s" if len(test_objects) > 1 else ""}', f'Disk I/O Benchmark{"s" if len(test_objects) > 1 else ""}',
@ -1228,7 +1231,8 @@ def network_test():
std.pause('Press Enter to return to main menu...') std.pause('Press Enter to return to main menu...')
def ost_build_report(dev, dev_type): def ost_build_report(dev, dev_type, state):
# pylint: disable=too-many-branches
"""Build report for posting to osTicket, returns str.""" """Build report for posting to osTicket, returns str."""
report = [] report = []
@ -1279,6 +1283,16 @@ def ost_build_report(dev, dev_type):
) )
else: else:
report.extend(ost_convert_report(test.report, start_index=1)) report.extend(ost_convert_report(test.report, start_index=1))
if name == 'Disk I/O Benchmark':
try:
image_path = graph.export_io_graph(dev, state.log_dir)
imgur_url = graph.upload_to_imgur(image_path)
nextcloud_url = graph.upload_to_nextcloud(
image_path, state.ost.ticket_id, dev.path.name)
report.append(f'Imgur: {imgur_url}')
report.append(f'Nextcloud: {nextcloud_url}')
except (AttributeError, RuntimeError):
report.append('Error(s) exporting graph')
# Spacer # Spacer
report.append('') report.append('')
@ -1391,7 +1405,7 @@ def ost_post_disk_results(state):
std.print_info('Posting results to osTicket...') std.print_info('Posting results to osTicket...')
for disk in state.disks: for disk in state.disks:
state.ost.post_response( state.ost.post_response(
ost_build_report(disk, 'Disk'), ost_build_report(disk, 'Disk', state),
color='Diags FAIL' if disk.any_test_failed() else 'Diags', color='Diags FAIL' if disk.any_test_failed() else 'Diags',
) )