Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions scripts/startup-graph/graph_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import json
import pandas as pd
import plotly.express as px
import numpy as np
import argparse


def readable_time(nanoseconds):
if nanoseconds >= 1e9:
return f"{nanoseconds / 1e9:.2f} s"
elif nanoseconds >= 1e6:
return f"{nanoseconds / 1e6:.2f} ms"
elif nanoseconds >= 1e3:
return f"{nanoseconds / 1e3:.2f} μs"
else:
return f"{nanoseconds} ns"


parser = argparse.ArgumentParser(description="Generate a Gantt chart of MicroShift internal services startup times.")
parser.add_argument(
"file_path",
help=(
"The path to the JSON file containing MicroShift startup data. "
"Data can be extracted from MicroShift logs by using:\n\n"
" sudo journalctl -u microshift | grep 'Startup data' | grep -oP '\\{.*\\}' > data.json"
), # noqa: W605
)


args = parser.parse_args()

with open(args.file_path, "r") as f:
json_data = json.load(f)

services = json_data["services"]
microshift = json_data["microshift"]
microshift_start = pd.to_datetime(microshift["start"])
microshift_serv_start = pd.to_datetime(microshift["servicesStart"])
microshift_ready = pd.to_datetime(microshift["ready"])
microshift_timeToReady = readable_time(microshift["timeToReady"])

df = pd.DataFrame(services)

df["start"] = pd.to_datetime(df["start"])
df["ready"] = pd.to_datetime(df["ready"])
df["adjustedReady"] = df["ready"]
df["timeToReady_ns"] = df["timeToReady"]
df["timeToReady"] = df["timeToReady"].apply(readable_time)
df["name_time"] = df.apply(lambda row: f"{row['name']} ({row['timeToReady']}) ", axis=1)

# adjust all lines to have minimum thickness
min_width = pd.Timedelta(milliseconds=30)

df["adjustedReady"] = np.where(
(df["ready"] - df["start"]) < min_width,
df["start"] + min_width,
df["ready"]
)

custom_color_scale = [
(0.0, "#2E7F18"),
(0.4, "#45731E"),
(0.6, "#675E24"),
(0.7, "#8D472B"),
(0.9, "#B13433"),
(1.0, "#C82538"),
]

fig = px.timeline(
df,
x_start="start",
x_end="adjustedReady",
y="name_time",
color="timeToReady_ns",
hover_data={
"name": True,
"start": False,
"ready": False,
"adjustedReady": False,
"timeToReady": True,
"timeToReady_ns": False,
"dependencies": True,
"name_time": False
},
color_continuous_scale=custom_color_scale,
labels={"name": "Service"}
)

fig.update_yaxes(autorange="reversed")
fig.update_layout(coloraxis_showscale=False)

fig.update_layout(
xaxis=dict(type="date", range=[microshift_start - pd.Timedelta(milliseconds=200), microshift_ready + pd.Timedelta(milliseconds=200)]),
)

fig.add_vline(x=microshift_start, line_dash="dash")
fig.add_vline(x=microshift_ready, line_dash="dash")

fig.add_annotation(
x=microshift_start,
y=1.01,
yref="paper",
text="MicroShift start",
showarrow=False,
xanchor="center",
yanchor="bottom",
font=dict(size=12, color="black")
)

fig.add_annotation(
x=microshift_ready,
y=1.01,
yref="paper",
text=f"MicroShift ready ({microshift_timeToReady})",
showarrow=False,
xanchor="center",
yanchor="bottom",
font=dict(size=12, color="black")
)

fig.show()
3 changes: 3 additions & 0 deletions scripts/startup-graph/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
numpy>=2
pandas==2.2.3
plotly==5.24.1
80 changes: 80 additions & 0 deletions scripts/startup-graph/startup_graph.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")"
ROOT_DIR=$(realpath "${SCRIPT_DIR}/../..")

action_get_data() {
local path="${1}"
if [ -z "${path}" ] ; then
echo "ERROR: No filename specified." 1>&2
exit 1
fi

sudo journalctl -u microshift | grep 'Startup data' | grep -oP '\{.*\}' | tail -n 1 > "${path}"
}

action_generate() {
local path="${1}"
if [ -z "${path}" ] ; then
echo "ERROR: No data file specified." 1>&2
exit 1
fi

local venv="${ROOT_DIR}/_output/graphenv"

if [ ! -d "${venv}" ] ; then
python3 -m venv "${venv}"
"${venv}/bin/python3" -m pip install --upgrade pip
"${venv}/bin/python3" -m pip install -r "${SCRIPT_DIR}/requirements.txt"
fi

"${venv}/bin/python3" "${SCRIPT_DIR}/graph_gen.py" "${path}"
}

action_cleanup() {
local venv="${ROOT_DIR}/_output/graphenv"

rm -rf "${venv}"
}

usage() {
cat - <<EOF
${BASH_SOURCE[0]} (generate|get-data|cleanup) data.json

-h Show this help.

generate data.json
generate Gantt chart from specified JSON file

get-data data.json
extract MicroShift internal service startup times
from journalctl and save to data.json

cleanup
delete python virtual environment

EOF
}

if [ $# -eq 0 ]; then
usage
exit 1
fi
action="${1//-/_}"
shift

case "${action}" in
get_data|generate|cleanup)
"action_${action}" "$@"
;;
-h)
usage
exit 0
;;
*)
usage
exit 1
;;
esac