Skip to content

Commit 88edaf5

Browse files
committed
add psi performance test
1 parent 2fa4851 commit 88edaf5

File tree

4 files changed

+739
-0
lines changed

4 files changed

+739
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import json
2+
import os
3+
import re
4+
import sys
5+
from collections import defaultdict
6+
7+
if len(sys.argv) < 2:
8+
print("Usage: python3 analyze_results.py <log_directory>")
9+
sys.exit(1)
10+
11+
LOG_DIR = sys.argv[1]
12+
13+
def parse_top_node_log(file_path):
14+
"""Parses the output of 'kubectl top node'."""
15+
with open(file_path, 'r') as f:
16+
lines = f.readlines()
17+
if len(lines) < 2:
18+
return None, None
19+
20+
# Skip header
21+
parts = lines[1].split()
22+
23+
cpu_raw = parts[1]
24+
mem_raw = parts[3]
25+
26+
cpu_cores = int(cpu_raw.replace('m', ''))
27+
28+
mem_kib = 0
29+
if 'Ki' in mem_raw:
30+
mem_kib = int(mem_raw.replace('Ki', ''))
31+
elif 'Mi' in mem_raw:
32+
mem_mib_raw = int(mem_raw.replace('Mi', ''))
33+
mem_kib = mem_mib_raw * 1024
34+
35+
mem_mib = mem_kib / 1024
36+
37+
return cpu_cores, mem_mib
38+
39+
def parse_stats_summary(file_path):
40+
"""Parses the kubelet stats/summary JSON."""
41+
with open(file_path, 'r') as f:
42+
data = json.load(f)
43+
44+
kubelet_stats = next((sc for sc in data.get("node", {}).get("systemContainers", []) if sc["name"] == "kubelet"), None)
45+
if not kubelet_stats:
46+
return None, None
47+
48+
cpu_nanocores = kubelet_stats.get("cpu", {}).get("usageNanoCores", 0)
49+
mem_workingset_bytes = kubelet_stats.get("memory", {}).get("workingSetBytes", 0)
50+
51+
return cpu_nanocores, mem_workingset_bytes / (1024**2) # Convert to MiB
52+
53+
# Helper to calculate delta
54+
def get_delta(base, test, unit, is_cpu=False):
55+
if base is None or test is None:
56+
return "N/A"
57+
delta = test - base
58+
percent = (delta / base * 100) if base != 0 else 0
59+
sign = "+" if delta >= 0 else ""
60+
61+
# Convert nanocores to millicores for CPU
62+
if is_cpu:
63+
base /= 1_000_000
64+
test /= 1_000_000
65+
delta /= 1_000_000
66+
67+
return f"{base:.2f} -> {test:.2f} ({sign}{delta:.2f} / {sign}{percent:.2f}%)"
68+
69+
def main():
70+
results = defaultdict(lambda: defaultdict(dict))
71+
72+
for filename in os.listdir(LOG_DIR):
73+
if not filename.endswith(".log") and not filename.endswith(".json"):
74+
continue
75+
76+
parts = filename.split('_')
77+
78+
feature_status = "enabled" if "enabled" in filename else "disabled"
79+
stress_type = "unknown"
80+
if "cpu" in filename:
81+
stress_type = "cpu"
82+
elif "memory" in filename:
83+
stress_type = "memory"
84+
elif "io" in filename:
85+
stress_type = "io"
86+
87+
condition = "unknown"
88+
if "idle" in filename:
89+
condition = "idle"
90+
elif "load" in filename:
91+
condition = "load"
92+
93+
file_path = os.path.join(LOG_DIR, filename)
94+
95+
if "top_node" in filename:
96+
cpu, mem = parse_top_node_log(file_path)
97+
results[stress_type][feature_status][f"{condition}_node_cpu_m"] = cpu
98+
results[stress_type][feature_status][f"{condition}_node_mem_mib"] = mem
99+
elif "stats_summary" in filename:
100+
cpu, mem = parse_stats_summary(file_path)
101+
results[stress_type][feature_status][f"{condition}_kubelet_cpu_nanocores"] = cpu
102+
results[stress_type][feature_status][f"{condition}_kubelet_mem_mib"] = mem
103+
104+
print("--- Performance Analysis Summary by kubectl top node and /proxy/stats/summary---")
105+
106+
for stress_type, data in sorted(results.items()):
107+
print(f"\n### Stress Type: {stress_type.upper()}\n")
108+
109+
print("| Metric | Condition | Result (Baseline -> Test) |")
110+
print("|------------------------|-----------|---------------------------|")
111+
# Node Metrics
112+
idle_node_cpu = get_delta(data["disabled"].get("idle_node_cpu_m"), data["enabled"].get("idle_node_cpu_m"), "m")
113+
load_node_cpu = get_delta(data["disabled"].get("load_node_cpu_m"), data["enabled"].get("load_node_cpu_m"), "m")
114+
idle_node_mem = get_delta(data["disabled"].get("idle_node_mem_mib"), data["enabled"].get("idle_node_mem_mib"), "MiB")
115+
load_node_mem = get_delta(data["disabled"].get("load_node_mem_mib"), data["enabled"].get("load_node_mem_mib"), "MiB")
116+
117+
print(f"| **Node CPU (m)** | Idle | {idle_node_cpu} |")
118+
print(f"| **Node CPU (m)** | Load | {load_node_cpu} |")
119+
print(f"| **Node Memory (MiB)** | Idle | {idle_node_mem} |")
120+
print(f"| **Node Memory (MiB)** | Load | {load_node_mem} |")
121+
122+
# Kubelet Metrics
123+
idle_kubelet_cpu = get_delta(data["disabled"].get("idle_kubelet_cpu_nanocores"), data["enabled"].get("idle_kubelet_cpu_nanocores"), "m", is_cpu=True)
124+
load_kubelet_cpu = get_delta(data["disabled"].get("load_kubelet_cpu_nanocores"), data["enabled"].get("load_kubelet_cpu_nanocores"), "m", is_cpu=True)
125+
idle_kubelet_mem = get_delta(data["disabled"].get("idle_kubelet_mem_mib"), data["enabled"].get("idle_kubelet_mem_mib"), "MiB")
126+
load_kubelet_mem = get_delta(data["disabled"].get("load_kubelet_mem_mib"), data["enabled"].get("load_kubelet_mem_mib"), "MiB")
127+
128+
print(f"| **Kubelet CPU (m)** | Idle | {idle_kubelet_cpu} |")
129+
print(f"| **Kubelet CPU (m)** | Load | {load_kubelet_cpu} |")
130+
print(f"| **Kubelet Memory (MiB)**| Idle | {idle_kubelet_mem} |")
131+
print(f"| **Kubelet Memory (MiB)**| Load | {load_kubelet_mem} |")
132+
133+
if __name__ == "__main__":
134+
main()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
data:
4+
config.yaml: |
5+
prometheusK8s:
6+
retention: ${PROMETHEUS_RETENTION_PERIOD}
7+
nodeSelector:
8+
node-role.kubernetes.io/worker: ""
9+
volumeClaimTemplate:
10+
spec:
11+
storageClassName: ${STORAGE_CLASS}
12+
resources:
13+
requests:
14+
storage: ${PROMETHEUS_STORAGE_SIZE}
15+
alertmanagerMain:
16+
nodeSelector:
17+
node-role.kubernetes.io/worker: ""
18+
volumeClaimTemplate:
19+
spec:
20+
storageClassName: ${STORAGE_CLASS}
21+
resources:
22+
requests:
23+
storage: ${ALERTMANAGER_STORAGE_SIZE}
24+
metadata:
25+
name: cluster-monitoring-config
26+
namespace: openshift-monitoring

0 commit comments

Comments
 (0)