Added I/O Benchmark PNG graph sections
This commit is contained in:
parent
8643ec2c7c
commit
701d647a91
3 changed files with 136 additions and 3 deletions
|
|
@ -28,6 +28,8 @@ CRASH_SERVER = {
|
|||
'Pass': '',
|
||||
'Headers': {'X-Requested-With': 'XMLHttpRequest'},
|
||||
}
|
||||
# Misc
|
||||
IMGUR_CLIENT_ID='3d1ee1d38707b85'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -2,11 +2,24 @@
|
|||
# pylint: disable=bad-whitespace
|
||||
# vim: sts=2 sw=2 ts=2
|
||||
|
||||
import base64
|
||||
import json
|
||||
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
|
||||
|
||||
|
||||
# Hack to hide X11 error when running in CLI mode
|
||||
Gnuplot.gp.GnuplotOpts.default_term = 'xterm'
|
||||
|
||||
|
||||
# STATIC VARIABLES
|
||||
LOG = logging.getLogger(__name__)
|
||||
GRAPH_HORIZONTAL = ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█')
|
||||
|
|
@ -34,6 +47,48 @@ THRESH_GREAT = 750 * 1024**2
|
|||
|
||||
|
||||
# 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):
|
||||
"""Generate horizontal graph from rate_list, returns list."""
|
||||
graph = ['', '', '', '']
|
||||
|
|
@ -111,6 +166,68 @@ def merge_rates(rates, graph_width=40):
|
|||
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):
|
||||
"""Build colored graph string using thresholds, returns str."""
|
||||
color_bar = None
|
||||
|
|
|
|||
|
|
@ -748,7 +748,7 @@ def cpu_mprime_test(state, test_objects):
|
|||
std.print_info('Posting results to osTicket...')
|
||||
test_cooling_obj.cpu_max_temp = sensors.cpu_max_temp()
|
||||
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',
|
||||
)
|
||||
|
||||
|
|
@ -860,6 +860,9 @@ def disk_io_benchmark(state, test_objects, skip_usb=True):
|
|||
# Check results
|
||||
check_io_benchmark_results(test_obj, read_rates, IO_GRAPH_WIDTH)
|
||||
|
||||
# osTicket
|
||||
test_obj.read_rates = read_rates
|
||||
|
||||
# Run benchmarks
|
||||
state.update_top_pane(
|
||||
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...')
|
||||
|
||||
|
||||
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."""
|
||||
report = []
|
||||
|
||||
|
|
@ -1279,6 +1283,16 @@ def ost_build_report(dev, dev_type):
|
|||
)
|
||||
else:
|
||||
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
|
||||
report.append('')
|
||||
|
|
@ -1391,7 +1405,7 @@ def ost_post_disk_results(state):
|
|||
std.print_info('Posting results to osTicket...')
|
||||
for disk in state.disks:
|
||||
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',
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue