All checks were successful
Run Check Script / check (pull_request) Successful in 1m3s
150 lines
5.5 KiB
Python
150 lines
5.5 KiB
Python
import dash
|
|
from dash import dcc, html, Input, Output
|
|
import plotly.express as px
|
|
import pandas as pd
|
|
import dash_bootstrap_components as dbc
|
|
import io
|
|
|
|
# --- Sample Data ---
|
|
# In a real-world scenario, you would load this from a CSV file.
|
|
# For this self-contained example, we define the data directly.
|
|
# Example: df = pd.read_csv('benchmark_data.csv')
|
|
csv_data = """
|
|
config,op_type,iops,latency_ms,throughput_mbs
|
|
All-HDD,4k_random_read,260,60,1.02
|
|
All-HDD,4k_random_write,100,150,0.39
|
|
All-HDD,64k_sequential_read,2100,30,131.25
|
|
All-HDD,64k_sequential_write,1500,42,93.75
|
|
HDD+SSD_WAL,4k_random_read,270,58,1.05
|
|
HDD+SSD_WAL,4k_random_write,160,100,0.62
|
|
HDD+SSD_WAL,64k_sequential_read,2150,29,134.37
|
|
HDD+SSD_WAL,64k_sequential_write,1800,35,112.5
|
|
HDD+SSD_WAL_DB,4k_random_read,1250,12,4.88
|
|
HDD+SSD_WAL_DB,4k_random_write,1550,10,6.05
|
|
HDD+SSD_WAL_DB,64k_sequential_read,2200,28,137.5
|
|
HDD+SSD_WAL_DB,64k_sequential_write,2000,32,125
|
|
All-NVMe,4k_random_read,400000,0.1,1562.5
|
|
All-NVMe,4k_random_write,350000,0.12,1367.18
|
|
All-NVMe,64k_sequential_read,16000,4,1000
|
|
All-NVMe,64k_sequential_write,12500,5,800
|
|
"""
|
|
|
|
# Read the data using pandas
|
|
df = pd.read_csv(io.StringIO(csv_data))
|
|
|
|
# Initialize the Dash app with a Bootstrap theme
|
|
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.FLATLY])
|
|
|
|
# --- App Layout ---
|
|
app.layout = dbc.Container([
|
|
# Header
|
|
dbc.Row([
|
|
dbc.Col([
|
|
html.H1("Ceph Cluster Benchmark Visualizer", className="text-primary"),
|
|
html.P(
|
|
"An interactive tool to compare performance metrics across different Ceph storage configurations.",
|
|
className="lead"
|
|
)
|
|
])
|
|
], className="my-4"),
|
|
|
|
# Controls and Graphs Row
|
|
dbc.Row([
|
|
# Control Panel Column
|
|
dbc.Col([
|
|
dbc.Card([
|
|
dbc.CardBody([
|
|
html.H4("Control Panel", className="card-title"),
|
|
html.Hr(),
|
|
|
|
# Metric Selection Radio Buttons
|
|
dbc.Label("Select Metric to Display:", html_for="metric-selector"),
|
|
dcc.RadioItems(
|
|
id='metric-selector',
|
|
options=[
|
|
{'label': 'IOPS (Input/Output Operations Per Second)', 'value': 'iops'},
|
|
{'label': 'Latency (in Milliseconds)', 'value': 'latency_ms'},
|
|
{'label': 'Throughput (in MB/s)', 'value': 'throughput_mbs'}
|
|
],
|
|
value='iops', # Default value
|
|
labelClassName="d-block" # Display labels as blocks
|
|
),
|
|
html.Hr(),
|
|
|
|
# Configuration Selection Checklist
|
|
dbc.Label("Select Configurations to Compare:", html_for="config-checklist"),
|
|
dcc.Checklist(
|
|
id='config-checklist',
|
|
options=[{'label': config, 'value': config} for config in df['config'].unique()],
|
|
value=df['config'].unique(), # Select all by default
|
|
labelClassName="d-block"
|
|
),
|
|
])
|
|
], className="mb-4")
|
|
], width=12, lg=4), # Full width on small screens, 1/3 on large
|
|
|
|
# Graph Display Column
|
|
dbc.Col([
|
|
dcc.Graph(id='benchmark-graph')
|
|
], width=12, lg=8) # Full width on small screens, 2/3 on large
|
|
])
|
|
], fluid=True) # Use a fluid container for full-width layout
|
|
|
|
|
|
# --- Callback Function ---
|
|
# This function connects the controls to the graph
|
|
@app.callback(
|
|
Output('benchmark-graph', 'figure'),
|
|
[Input('metric-selector', 'value'),
|
|
Input('config-checklist', 'value')]
|
|
)
|
|
def update_graph(selected_metric, selected_configs):
|
|
"""
|
|
This function is triggered whenever a control's value changes.
|
|
It filters the dataframe and returns an updated bar chart figure.
|
|
"""
|
|
if not selected_configs:
|
|
# If no configs are selected, return an empty figure to avoid errors
|
|
return go.Figure().update_layout(
|
|
title="Please select a configuration to view data.",
|
|
xaxis_title="",
|
|
yaxis_title=""
|
|
)
|
|
|
|
# Filter the DataFrame based on the selected configurations
|
|
filtered_df = df[df['config'].isin(selected_configs)]
|
|
|
|
# Create the bar chart using Plotly Express
|
|
fig = px.bar(
|
|
filtered_df,
|
|
x='op_type',
|
|
y=selected_metric,
|
|
color='config',
|
|
barmode='group', # Group bars for different configs side-by-side
|
|
labels={
|
|
"op_type": "Benchmark Operation Type",
|
|
"iops": "IOPS (Higher is Better)",
|
|
"latency_ms": "Latency in ms (Lower is Better)",
|
|
"throughput_mbs": "Throughput in MB/s (Higher is Better)",
|
|
"config": "Storage Configuration"
|
|
},
|
|
title=f"Benchmark Comparison for: {selected_metric.replace('_', ' ').title()}",
|
|
height=600 # Set a fixed height for the graph
|
|
)
|
|
|
|
# Update layout for better readability
|
|
fig.update_layout(
|
|
xaxis_title="Operation Type",
|
|
yaxis_title=selected_metric.replace('_', ' ').title(),
|
|
legend_title="Configuration",
|
|
title_x=0.5, # Center the title
|
|
xaxis={'categoryorder':'total descending' if selected_metric != 'latency_ms' else 'total ascending'}
|
|
)
|
|
|
|
return fig
|
|
|
|
# --- Run the App ---
|
|
if __name__ == '__main__':
|
|
# Use debug=True for development, allowing hot-reloading
|
|
app.run(debug=True)
|