tunectl 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/bin/tunectl +43 -0
- package/package.json +33 -0
- package/scripts/audit.sh +693 -0
- package/scripts/benchmark.sh +623 -0
- package/scripts/discover.sh +367 -0
- package/scripts/rollback.sh +267 -0
- package/scripts/tune.sh +1073 -0
- package/setup.py +5 -0
- package/tune-manifest.json +993 -0
- package/tunectl/__init__.py +3 -0
- package/tunectl/__main__.py +6 -0
- package/tunectl/cli.py +433 -0
package/scripts/audit.sh
ADDED
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -uo pipefail
|
|
3
|
+
|
|
4
|
+
# audit.sh — Verification script for tunectl
|
|
5
|
+
# Checks all manifest entries against live system state.
|
|
6
|
+
# Strictly read-only: no system modifications.
|
|
7
|
+
#
|
|
8
|
+
# Output format: one line per check with [ENTRY-ID] PASS/FAIL/SKIP parameter expected=X actual=Y
|
|
9
|
+
# Summary line: X/Y checks passed
|
|
10
|
+
#
|
|
11
|
+
# Exit codes: 0=all pass, 1=any fail, 2=usage error
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
MANIFEST="${SCRIPT_DIR}/../tune-manifest.json"
|
|
15
|
+
|
|
16
|
+
# -------------------------------------------------------
|
|
17
|
+
# Validate manifest
|
|
18
|
+
# -------------------------------------------------------
|
|
19
|
+
if [[ ! -f "$MANIFEST" ]]; then
|
|
20
|
+
echo "Error: Manifest not found at $MANIFEST" >&2
|
|
21
|
+
exit 2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if ! jq empty "$MANIFEST" 2>/dev/null; then
|
|
25
|
+
echo "Error: Manifest is not valid JSON: $MANIFEST" >&2
|
|
26
|
+
exit 2
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Verify manifest has tuning_entries array
|
|
30
|
+
entry_count=$(jq '.tuning_entries | length' "$MANIFEST" 2>/dev/null)
|
|
31
|
+
if [[ -z "$entry_count" || "$entry_count" -eq 0 ]]; then
|
|
32
|
+
echo "Error: Manifest has no tuning_entries" >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# -------------------------------------------------------
|
|
37
|
+
# Counters
|
|
38
|
+
# -------------------------------------------------------
|
|
39
|
+
PASS_COUNT=0
|
|
40
|
+
FAIL_COUNT=0
|
|
41
|
+
SKIP_COUNT=0
|
|
42
|
+
TOTAL=0
|
|
43
|
+
|
|
44
|
+
# -------------------------------------------------------
|
|
45
|
+
# Output helpers
|
|
46
|
+
# -------------------------------------------------------
|
|
47
|
+
emit_pass() {
|
|
48
|
+
local id="$1" parameter="$2" expected="$3" actual="$4"
|
|
49
|
+
echo "[$id] PASS $parameter expected=$expected actual=$actual"
|
|
50
|
+
PASS_COUNT=$((PASS_COUNT + 1))
|
|
51
|
+
TOTAL=$((TOTAL + 1))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
emit_fail() {
|
|
55
|
+
local id="$1" parameter="$2" expected="$3" actual="$4"
|
|
56
|
+
echo "[$id] FAIL $parameter expected=$expected actual=$actual"
|
|
57
|
+
FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
58
|
+
TOTAL=$((TOTAL + 1))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
emit_skip() {
|
|
62
|
+
local id="$1" parameter="$2" reason="$3"
|
|
63
|
+
echo "[$id] SKIP $parameter expected=N/A actual=$reason"
|
|
64
|
+
SKIP_COUNT=$((SKIP_COUNT + 1))
|
|
65
|
+
TOTAL=$((TOTAL + 1))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# -------------------------------------------------------
|
|
69
|
+
# Normalize whitespace for comparison
|
|
70
|
+
# Trims leading/trailing whitespace, collapses internal whitespace
|
|
71
|
+
# -------------------------------------------------------
|
|
72
|
+
normalize() {
|
|
73
|
+
echo "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -s '[:space:]' ' '
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# -------------------------------------------------------
|
|
77
|
+
# Read live sysctl value from /proc/sys (NOT config files)
|
|
78
|
+
# This satisfies VAL-AUDIT-012
|
|
79
|
+
# -------------------------------------------------------
|
|
80
|
+
get_live_sysctl() {
|
|
81
|
+
local param="$1"
|
|
82
|
+
local proc_path="/proc/sys/$(echo "$param" | tr '.' '/')"
|
|
83
|
+
if [[ -r "$proc_path" ]]; then
|
|
84
|
+
cat "$proc_path" 2>/dev/null | tr '\t' ' ' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -s ' '
|
|
85
|
+
else
|
|
86
|
+
echo ""
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# -------------------------------------------------------
|
|
91
|
+
# Check a sysctl entry
|
|
92
|
+
# -------------------------------------------------------
|
|
93
|
+
check_sysctl() {
|
|
94
|
+
local id="$1" parameter="$2" expected="$3" config_file="$4"
|
|
95
|
+
# Verify the referenced config file exists before checking live value
|
|
96
|
+
if [[ ! -f "$config_file" ]]; then
|
|
97
|
+
emit_fail "$id" "$parameter" "$expected" "config file missing: $config_file"
|
|
98
|
+
return
|
|
99
|
+
fi
|
|
100
|
+
local actual
|
|
101
|
+
actual=$(get_live_sysctl "$parameter")
|
|
102
|
+
if [[ -z "$actual" ]]; then
|
|
103
|
+
emit_fail "$id" "$parameter" "$expected" "not readable"
|
|
104
|
+
return
|
|
105
|
+
fi
|
|
106
|
+
local norm_expected norm_actual
|
|
107
|
+
norm_expected=$(normalize "$expected")
|
|
108
|
+
norm_actual=$(normalize "$actual")
|
|
109
|
+
if [[ "$norm_actual" == "$norm_expected" ]]; then
|
|
110
|
+
emit_pass "$id" "$parameter" "$expected" "$actual"
|
|
111
|
+
else
|
|
112
|
+
emit_fail "$id" "$parameter" "$expected" "$actual"
|
|
113
|
+
fi
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# -------------------------------------------------------
|
|
117
|
+
# Check service disabled/masked entries (SVC-*)
|
|
118
|
+
# Non-existent services → SKIP (VAL-TUNE-029)
|
|
119
|
+
# -------------------------------------------------------
|
|
120
|
+
check_service_disabled() {
|
|
121
|
+
local id="$1" parameter="$2"
|
|
122
|
+
# Check if the service unit file exists at all
|
|
123
|
+
if ! systemctl list-unit-files "$parameter" 2>/dev/null | grep -qF "$parameter"; then
|
|
124
|
+
emit_skip "$id" "$parameter" "service_not_installed"
|
|
125
|
+
return
|
|
126
|
+
fi
|
|
127
|
+
local enabled
|
|
128
|
+
enabled=$(systemctl is-enabled "$parameter" 2>/dev/null) || true
|
|
129
|
+
if [[ "$enabled" == "masked" || "$enabled" == "disabled" ]]; then
|
|
130
|
+
emit_pass "$id" "$parameter" "disabled/masked" "$enabled"
|
|
131
|
+
else
|
|
132
|
+
emit_fail "$id" "$parameter" "disabled/masked" "$enabled"
|
|
133
|
+
fi
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# -------------------------------------------------------
|
|
137
|
+
# Check service active (CPU-003 irqbalance)
|
|
138
|
+
# -------------------------------------------------------
|
|
139
|
+
check_service_active() {
|
|
140
|
+
local id="$1" parameter="$2" expected_state="$3"
|
|
141
|
+
# Extract service name from parameter (e.g. "irqbalance service" → "irqbalance")
|
|
142
|
+
local svc_name
|
|
143
|
+
svc_name=$(echo "$parameter" | awk '{print $1}')
|
|
144
|
+
# Check if the service exists
|
|
145
|
+
if ! systemctl list-unit-files "${svc_name}.service" 2>/dev/null | grep -qF "${svc_name}"; then
|
|
146
|
+
emit_skip "$id" "$parameter" "service_not_installed"
|
|
147
|
+
return
|
|
148
|
+
fi
|
|
149
|
+
local actual
|
|
150
|
+
actual=$(systemctl is-active "${svc_name}.service" 2>/dev/null) || true
|
|
151
|
+
# The after_value is "active (kept)" — we check if active
|
|
152
|
+
if [[ "$actual" == "active" ]]; then
|
|
153
|
+
emit_pass "$id" "$parameter" "active" "$actual"
|
|
154
|
+
else
|
|
155
|
+
emit_fail "$id" "$parameter" "active" "$actual"
|
|
156
|
+
fi
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# -------------------------------------------------------
|
|
160
|
+
# Check fstab entries (SWAP-005, FS-001, FS-002, FS-003)
|
|
161
|
+
# -------------------------------------------------------
|
|
162
|
+
check_fstab_entry() {
|
|
163
|
+
local id="$1" parameter="$2" after_value="$3"
|
|
164
|
+
local fstab="/etc/fstab"
|
|
165
|
+
|
|
166
|
+
if [[ ! -f "$fstab" ]]; then
|
|
167
|
+
emit_fail "$id" "$parameter" "$after_value" "fstab_missing"
|
|
168
|
+
return
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
case "$id" in
|
|
172
|
+
SWAP-005)
|
|
173
|
+
# Check that /dev/zram0 swap entry exists in fstab
|
|
174
|
+
if grep -q '/dev/zram0' "$fstab" 2>/dev/null; then
|
|
175
|
+
local actual_line
|
|
176
|
+
actual_line=$(grep '/dev/zram0' "$fstab" | head -1 | sed 's/[[:space:]]*$//')
|
|
177
|
+
emit_pass "$id" "$parameter" "$after_value" "$actual_line"
|
|
178
|
+
else
|
|
179
|
+
emit_fail "$id" "$parameter" "$after_value" "not_present"
|
|
180
|
+
fi
|
|
181
|
+
;;
|
|
182
|
+
FS-001)
|
|
183
|
+
# Check root mount has noatime
|
|
184
|
+
local root_line
|
|
185
|
+
root_line=$(grep -E '^\S+\s+/\s' "$fstab" 2>/dev/null | head -1 || true)
|
|
186
|
+
if [[ -z "$root_line" ]]; then
|
|
187
|
+
emit_fail "$id" "$parameter" "$after_value" "root_mount_not_found"
|
|
188
|
+
elif echo "$root_line" | grep -q 'noatime'; then
|
|
189
|
+
emit_pass "$id" "$parameter" "noatime" "noatime"
|
|
190
|
+
else
|
|
191
|
+
local opts
|
|
192
|
+
opts=$(echo "$root_line" | awk '{print $4}')
|
|
193
|
+
emit_fail "$id" "$parameter" "noatime" "$opts"
|
|
194
|
+
fi
|
|
195
|
+
;;
|
|
196
|
+
FS-002)
|
|
197
|
+
# Check root mount has commit=60
|
|
198
|
+
local root_line
|
|
199
|
+
root_line=$(grep -E '^\S+\s+/\s' "$fstab" 2>/dev/null | head -1 || true)
|
|
200
|
+
if [[ -z "$root_line" ]]; then
|
|
201
|
+
emit_fail "$id" "$parameter" "$after_value" "root_mount_not_found"
|
|
202
|
+
elif echo "$root_line" | grep -q 'commit=60'; then
|
|
203
|
+
emit_pass "$id" "$parameter" "commit=60" "commit=60"
|
|
204
|
+
else
|
|
205
|
+
local opts
|
|
206
|
+
opts=$(echo "$root_line" | awk '{print $4}')
|
|
207
|
+
emit_fail "$id" "$parameter" "commit=60" "$opts"
|
|
208
|
+
fi
|
|
209
|
+
;;
|
|
210
|
+
FS-003)
|
|
211
|
+
# Check tmpfs /tmp exists in fstab
|
|
212
|
+
if grep -qE 'tmpfs\s+/tmp' "$fstab" 2>/dev/null; then
|
|
213
|
+
local actual_line
|
|
214
|
+
actual_line=$(grep -E 'tmpfs\s+/tmp' "$fstab" | head -1 | sed 's/[[:space:]]*$//')
|
|
215
|
+
emit_pass "$id" "$parameter" "$after_value" "$actual_line"
|
|
216
|
+
else
|
|
217
|
+
emit_fail "$id" "$parameter" "$after_value" "not_present"
|
|
218
|
+
fi
|
|
219
|
+
;;
|
|
220
|
+
esac
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# -------------------------------------------------------
|
|
224
|
+
# Check udev rules (SWAP-006, SWAP-007, FS-004, FS-005)
|
|
225
|
+
# -------------------------------------------------------
|
|
226
|
+
check_udev_entry() {
|
|
227
|
+
local id="$1" parameter="$2" config_file="$3" after_value="$4"
|
|
228
|
+
|
|
229
|
+
if [[ ! -f "$config_file" ]]; then
|
|
230
|
+
emit_fail "$id" "$parameter" "$after_value" "file_missing"
|
|
231
|
+
return
|
|
232
|
+
fi
|
|
233
|
+
|
|
234
|
+
local content
|
|
235
|
+
content=$(cat "$config_file" 2>/dev/null || echo "")
|
|
236
|
+
|
|
237
|
+
case "$id" in
|
|
238
|
+
SWAP-006)
|
|
239
|
+
# zram0 comp_algorithm: check live /sys state FIRST, then verify udev rule
|
|
240
|
+
local live_comp live_comp_ok=false rule_comp_ok=false
|
|
241
|
+
live_comp=$(cat /sys/block/zram0/comp_algorithm 2>/dev/null || echo "not available")
|
|
242
|
+
if echo "$live_comp" | grep -q "\[$after_value\]"; then
|
|
243
|
+
live_comp_ok=true
|
|
244
|
+
fi
|
|
245
|
+
if echo "$content" | grep -q "comp_algorithm.*$after_value"; then
|
|
246
|
+
rule_comp_ok=true
|
|
247
|
+
fi
|
|
248
|
+
if $live_comp_ok && $rule_comp_ok; then
|
|
249
|
+
emit_pass "$id" "$parameter" "$after_value" "$after_value"
|
|
250
|
+
elif ! $live_comp_ok; then
|
|
251
|
+
emit_fail "$id" "$parameter" "$after_value" "live=$live_comp"
|
|
252
|
+
else
|
|
253
|
+
emit_fail "$id" "$parameter" "$after_value" "udev rule missing"
|
|
254
|
+
fi
|
|
255
|
+
;;
|
|
256
|
+
SWAP-007)
|
|
257
|
+
# zram0 disksize: check live /sys state FIRST, then verify udev rule
|
|
258
|
+
local live_disksize_bytes expected_bytes num
|
|
259
|
+
live_disksize_bytes=$(cat /sys/block/zram0/disksize 2>/dev/null || echo "0")
|
|
260
|
+
num="${after_value%G}"
|
|
261
|
+
expected_bytes=$((num * 1073741824))
|
|
262
|
+
local live_disk_ok=false rule_disk_ok=false
|
|
263
|
+
if [[ "$live_disksize_bytes" == "$expected_bytes" ]]; then
|
|
264
|
+
live_disk_ok=true
|
|
265
|
+
fi
|
|
266
|
+
if echo "$content" | grep -q "disksize.*$after_value"; then
|
|
267
|
+
rule_disk_ok=true
|
|
268
|
+
fi
|
|
269
|
+
if $live_disk_ok && $rule_disk_ok; then
|
|
270
|
+
emit_pass "$id" "$parameter" "$after_value" "${live_disksize_bytes}B"
|
|
271
|
+
elif ! $live_disk_ok; then
|
|
272
|
+
emit_fail "$id" "$parameter" "$after_value" "live=${live_disksize_bytes}B"
|
|
273
|
+
else
|
|
274
|
+
emit_fail "$id" "$parameter" "$after_value" "udev rule missing"
|
|
275
|
+
fi
|
|
276
|
+
;;
|
|
277
|
+
FS-004)
|
|
278
|
+
# I/O scheduler should be none
|
|
279
|
+
if echo "$content" | grep -q 'scheduler.*none\|scheduler}="none"'; then
|
|
280
|
+
# Also check live value
|
|
281
|
+
local live_sched
|
|
282
|
+
live_sched=$(cat /sys/block/vda/queue/scheduler 2>/dev/null || echo "not available")
|
|
283
|
+
if echo "$live_sched" | grep -q '\[none\]'; then
|
|
284
|
+
emit_pass "$id" "$parameter" "$after_value" "none"
|
|
285
|
+
else
|
|
286
|
+
emit_pass "$id" "$parameter" "$after_value" "rule_present"
|
|
287
|
+
fi
|
|
288
|
+
else
|
|
289
|
+
emit_fail "$id" "$parameter" "$after_value" "rule_not_found"
|
|
290
|
+
fi
|
|
291
|
+
;;
|
|
292
|
+
FS-005)
|
|
293
|
+
# read_ahead_kb should be 1024
|
|
294
|
+
if echo "$content" | grep -q 'read_ahead_kb.*1024\|read_ahead_kb}="1024"'; then
|
|
295
|
+
# Also check live value
|
|
296
|
+
local live_ra
|
|
297
|
+
live_ra=$(cat /sys/block/vda/queue/read_ahead_kb 2>/dev/null || echo "not available")
|
|
298
|
+
if [[ "$live_ra" == "1024" ]]; then
|
|
299
|
+
emit_pass "$id" "$parameter" "$after_value" "1024"
|
|
300
|
+
else
|
|
301
|
+
emit_pass "$id" "$parameter" "$after_value" "rule_present"
|
|
302
|
+
fi
|
|
303
|
+
else
|
|
304
|
+
emit_fail "$id" "$parameter" "$after_value" "rule_not_found"
|
|
305
|
+
fi
|
|
306
|
+
;;
|
|
307
|
+
esac
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# -------------------------------------------------------
|
|
311
|
+
# Check modules-load entries (NET-022, SWAP-008)
|
|
312
|
+
# -------------------------------------------------------
|
|
313
|
+
check_modules_load() {
|
|
314
|
+
local id="$1" parameter="$2" config_file="$3" after_value="$4"
|
|
315
|
+
|
|
316
|
+
if [[ ! -f "$config_file" ]]; then
|
|
317
|
+
emit_fail "$id" "$parameter" "$after_value" "file_missing"
|
|
318
|
+
return
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
local content
|
|
322
|
+
content=$(cat "$config_file" 2>/dev/null || echo "")
|
|
323
|
+
if echo "$content" | grep -q "$after_value"; then
|
|
324
|
+
emit_pass "$id" "$parameter" "$after_value" "$after_value"
|
|
325
|
+
else
|
|
326
|
+
emit_fail "$id" "$parameter" "$after_value" "$content"
|
|
327
|
+
fi
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
# -------------------------------------------------------
|
|
331
|
+
# Check GRUB entries (SWAP-009, BOOT-001..005)
|
|
332
|
+
# -------------------------------------------------------
|
|
333
|
+
check_grub_entry() {
|
|
334
|
+
local id="$1" parameter="$2" after_value="$3"
|
|
335
|
+
local grub_file="/etc/default/grub"
|
|
336
|
+
|
|
337
|
+
if [[ ! -f "$grub_file" ]]; then
|
|
338
|
+
emit_fail "$id" "$parameter" "$after_value" "grub_file_missing"
|
|
339
|
+
return
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
local grub_cmdline
|
|
343
|
+
grub_cmdline=$(grep '^GRUB_CMDLINE_LINUX_DEFAULT=' "$grub_file" 2>/dev/null | sed 's/^GRUB_CMDLINE_LINUX_DEFAULT=//' | tr -d '"' || echo "")
|
|
344
|
+
|
|
345
|
+
case "$id" in
|
|
346
|
+
SWAP-009)
|
|
347
|
+
# zswap.enabled=0 should be in GRUB cmdline
|
|
348
|
+
if echo "$grub_cmdline" | grep -q 'zswap.enabled=0'; then
|
|
349
|
+
emit_pass "$id" "$parameter" "zswap.enabled=0" "zswap.enabled=0"
|
|
350
|
+
else
|
|
351
|
+
emit_fail "$id" "$parameter" "zswap.enabled=0" "$grub_cmdline"
|
|
352
|
+
fi
|
|
353
|
+
;;
|
|
354
|
+
BOOT-001)
|
|
355
|
+
# mitigations=auto,nosmt
|
|
356
|
+
if echo "$grub_cmdline" | grep -q 'mitigations=auto,nosmt'; then
|
|
357
|
+
emit_pass "$id" "$parameter" "mitigations=auto,nosmt" "mitigations=auto,nosmt"
|
|
358
|
+
else
|
|
359
|
+
local found
|
|
360
|
+
found=$(echo "$grub_cmdline" | grep -oE 'mitigations=[^ ]*' || echo "not found")
|
|
361
|
+
emit_fail "$id" "$parameter" "mitigations=auto,nosmt" "$found"
|
|
362
|
+
fi
|
|
363
|
+
;;
|
|
364
|
+
BOOT-002)
|
|
365
|
+
# l1tf=off
|
|
366
|
+
if echo "$grub_cmdline" | grep -q 'l1tf=off'; then
|
|
367
|
+
emit_pass "$id" "$parameter" "l1tf=off" "l1tf=off"
|
|
368
|
+
else
|
|
369
|
+
local found
|
|
370
|
+
found=$(echo "$grub_cmdline" | grep -oE 'l1tf=[^ ]*' || echo "not found")
|
|
371
|
+
emit_fail "$id" "$parameter" "l1tf=off" "$found"
|
|
372
|
+
fi
|
|
373
|
+
;;
|
|
374
|
+
BOOT-003)
|
|
375
|
+
# tsx_async_abort=off
|
|
376
|
+
if echo "$grub_cmdline" | grep -q 'tsx_async_abort=off'; then
|
|
377
|
+
emit_pass "$id" "$parameter" "tsx_async_abort=off" "tsx_async_abort=off"
|
|
378
|
+
else
|
|
379
|
+
local found
|
|
380
|
+
found=$(echo "$grub_cmdline" | grep -oE 'tsx_async_abort=[^ ]*' || echo "not found")
|
|
381
|
+
emit_fail "$id" "$parameter" "tsx_async_abort=off" "$found"
|
|
382
|
+
fi
|
|
383
|
+
;;
|
|
384
|
+
BOOT-004)
|
|
385
|
+
# preempt=none
|
|
386
|
+
if echo "$grub_cmdline" | grep -q 'preempt=none'; then
|
|
387
|
+
emit_pass "$id" "$parameter" "preempt=none" "preempt=none"
|
|
388
|
+
else
|
|
389
|
+
local found
|
|
390
|
+
found=$(echo "$grub_cmdline" | grep -oE 'preempt=[^ ]*' || echo "not found")
|
|
391
|
+
emit_fail "$id" "$parameter" "preempt=none" "$found"
|
|
392
|
+
fi
|
|
393
|
+
;;
|
|
394
|
+
BOOT-005)
|
|
395
|
+
# transparent_hugepage=always
|
|
396
|
+
if echo "$grub_cmdline" | grep -q 'transparent_hugepage=always'; then
|
|
397
|
+
emit_pass "$id" "$parameter" "transparent_hugepage=always" "transparent_hugepage=always"
|
|
398
|
+
else
|
|
399
|
+
local found
|
|
400
|
+
found=$(echo "$grub_cmdline" | grep -oE 'transparent_hugepage=[^ ]*' || echo "not found")
|
|
401
|
+
emit_fail "$id" "$parameter" "transparent_hugepage=always" "$found"
|
|
402
|
+
fi
|
|
403
|
+
;;
|
|
404
|
+
esac
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
# -------------------------------------------------------
|
|
408
|
+
# Check tmpfiles.d entries (THP: MEM-012..014, KSM: MEM-015..018)
|
|
409
|
+
# These control /sys/kernel/mm/ values via systemd-tmpfiles
|
|
410
|
+
# We check the live /sys values (more authoritative)
|
|
411
|
+
# -------------------------------------------------------
|
|
412
|
+
check_thp_entry() {
|
|
413
|
+
local id="$1" parameter="$2" after_value="$3"
|
|
414
|
+
|
|
415
|
+
# Verify tmpfiles config exists
|
|
416
|
+
if [[ ! -f /etc/tmpfiles.d/thp.conf ]]; then
|
|
417
|
+
emit_fail "$id" "$parameter" "$after_value" "config file missing: /etc/tmpfiles.d/thp.conf"
|
|
418
|
+
return
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
case "$id" in
|
|
422
|
+
MEM-012)
|
|
423
|
+
# transparent_hugepage/enabled — check live value
|
|
424
|
+
local live
|
|
425
|
+
live=$(cat /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null || echo "not available")
|
|
426
|
+
if echo "$live" | grep -q "\[$after_value\]"; then
|
|
427
|
+
emit_pass "$id" "$parameter" "$after_value" "$after_value"
|
|
428
|
+
else
|
|
429
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
430
|
+
fi
|
|
431
|
+
;;
|
|
432
|
+
MEM-013)
|
|
433
|
+
# transparent_hugepage/defrag
|
|
434
|
+
local live
|
|
435
|
+
live=$(cat /sys/kernel/mm/transparent_hugepage/defrag 2>/dev/null || echo "not available")
|
|
436
|
+
if echo "$live" | grep -q "\[$after_value\]"; then
|
|
437
|
+
emit_pass "$id" "$parameter" "$after_value" "$after_value"
|
|
438
|
+
else
|
|
439
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
440
|
+
fi
|
|
441
|
+
;;
|
|
442
|
+
MEM-014)
|
|
443
|
+
# khugepaged/scan_sleep_millisecs
|
|
444
|
+
local live
|
|
445
|
+
live=$(cat /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs 2>/dev/null || echo "not available")
|
|
446
|
+
if [[ "$live" == "$after_value" ]]; then
|
|
447
|
+
emit_pass "$id" "$parameter" "$after_value" "$live"
|
|
448
|
+
else
|
|
449
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
450
|
+
fi
|
|
451
|
+
;;
|
|
452
|
+
esac
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
check_ksm_entry() {
|
|
456
|
+
local id="$1" parameter="$2" after_value="$3"
|
|
457
|
+
|
|
458
|
+
# Verify tmpfiles config exists
|
|
459
|
+
if [[ ! -f /etc/tmpfiles.d/ksm.conf ]]; then
|
|
460
|
+
emit_fail "$id" "$parameter" "$after_value" "config file missing: /etc/tmpfiles.d/ksm.conf"
|
|
461
|
+
return
|
|
462
|
+
fi
|
|
463
|
+
|
|
464
|
+
case "$id" in
|
|
465
|
+
MEM-015)
|
|
466
|
+
local live
|
|
467
|
+
live=$(cat /sys/kernel/mm/ksm/run 2>/dev/null || echo "not available")
|
|
468
|
+
if [[ "$live" == "$after_value" ]]; then
|
|
469
|
+
emit_pass "$id" "$parameter" "$after_value" "$live"
|
|
470
|
+
else
|
|
471
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
472
|
+
fi
|
|
473
|
+
;;
|
|
474
|
+
MEM-016)
|
|
475
|
+
local live
|
|
476
|
+
live=$(cat /sys/kernel/mm/ksm/pages_to_scan 2>/dev/null || echo "not available")
|
|
477
|
+
if [[ "$live" == "$after_value" ]]; then
|
|
478
|
+
emit_pass "$id" "$parameter" "$after_value" "$live"
|
|
479
|
+
else
|
|
480
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
481
|
+
fi
|
|
482
|
+
;;
|
|
483
|
+
MEM-017)
|
|
484
|
+
local live
|
|
485
|
+
live=$(cat /sys/kernel/mm/ksm/sleep_millisecs 2>/dev/null || echo "not available")
|
|
486
|
+
if [[ "$live" == "$after_value" ]]; then
|
|
487
|
+
emit_pass "$id" "$parameter" "$after_value" "$live"
|
|
488
|
+
else
|
|
489
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
490
|
+
fi
|
|
491
|
+
;;
|
|
492
|
+
MEM-018)
|
|
493
|
+
local live
|
|
494
|
+
live=$(cat /sys/kernel/mm/ksm/use_zero_pages 2>/dev/null || echo "not available")
|
|
495
|
+
if [[ "$live" == "$after_value" ]]; then
|
|
496
|
+
emit_pass "$id" "$parameter" "$after_value" "$live"
|
|
497
|
+
else
|
|
498
|
+
emit_fail "$id" "$parameter" "$after_value" "$live"
|
|
499
|
+
fi
|
|
500
|
+
;;
|
|
501
|
+
esac
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
# -------------------------------------------------------
|
|
505
|
+
# Check /etc/environment entries (MEM-019, BUILD-001)
|
|
506
|
+
# -------------------------------------------------------
|
|
507
|
+
check_environment_entry() {
|
|
508
|
+
local id="$1" parameter="$2" after_value="$3"
|
|
509
|
+
local env_file="/etc/environment"
|
|
510
|
+
|
|
511
|
+
if [[ ! -f "$env_file" ]]; then
|
|
512
|
+
emit_fail "$id" "$parameter" "$after_value" "file_missing"
|
|
513
|
+
return
|
|
514
|
+
fi
|
|
515
|
+
|
|
516
|
+
case "$id" in
|
|
517
|
+
MEM-019)
|
|
518
|
+
# LD_PRELOAD jemalloc
|
|
519
|
+
local actual
|
|
520
|
+
actual=$(grep '^LD_PRELOAD=' "$env_file" 2>/dev/null | cut -d= -f2- || echo "")
|
|
521
|
+
if [[ -z "$actual" ]]; then
|
|
522
|
+
actual="not_set"
|
|
523
|
+
fi
|
|
524
|
+
if [[ "$actual" == "$after_value" ]]; then
|
|
525
|
+
emit_pass "$id" "$parameter" "$after_value" "$actual"
|
|
526
|
+
elif echo "$actual" | grep -q 'libjemalloc'; then
|
|
527
|
+
emit_pass "$id" "$parameter" "$after_value" "$actual"
|
|
528
|
+
else
|
|
529
|
+
emit_fail "$id" "$parameter" "$after_value" "$actual"
|
|
530
|
+
fi
|
|
531
|
+
;;
|
|
532
|
+
BUILD-001)
|
|
533
|
+
# RUSTC_WRAPPER sccache
|
|
534
|
+
local actual
|
|
535
|
+
actual=$(grep '^RUSTC_WRAPPER=' "$env_file" 2>/dev/null | cut -d= -f2- || echo "")
|
|
536
|
+
if [[ -z "$actual" ]]; then
|
|
537
|
+
actual="not_set"
|
|
538
|
+
fi
|
|
539
|
+
if [[ "$actual" == "$after_value" ]]; then
|
|
540
|
+
emit_pass "$id" "$parameter" "$after_value" "$actual"
|
|
541
|
+
else
|
|
542
|
+
emit_fail "$id" "$parameter" "$after_value" "$actual"
|
|
543
|
+
fi
|
|
544
|
+
;;
|
|
545
|
+
esac
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
# -------------------------------------------------------
|
|
549
|
+
# Check systemd slice entries (CGRP-001..009)
|
|
550
|
+
# Check the unit file for expected property values
|
|
551
|
+
# -------------------------------------------------------
|
|
552
|
+
check_slice_entry() {
|
|
553
|
+
local id="$1" parameter="$2" config_file="$3" after_value="$4"
|
|
554
|
+
|
|
555
|
+
if [[ ! -f "$config_file" ]]; then
|
|
556
|
+
emit_fail "$id" "$parameter" "$after_value" "file_missing"
|
|
557
|
+
return
|
|
558
|
+
fi
|
|
559
|
+
|
|
560
|
+
local content
|
|
561
|
+
content=$(cat "$config_file" 2>/dev/null || echo "")
|
|
562
|
+
|
|
563
|
+
# Extract the property name from the parameter description
|
|
564
|
+
# e.g. "droid.slice CPUWeight" → look for CPUWeight=
|
|
565
|
+
local prop_name=""
|
|
566
|
+
case "$id" in
|
|
567
|
+
CGRP-001) prop_name="CPUWeight" ;;
|
|
568
|
+
CGRP-002) prop_name="MemoryHigh" ;;
|
|
569
|
+
CGRP-003) prop_name="IOWeight" ;;
|
|
570
|
+
CGRP-004) prop_name="ManagedOOMSwap" ;;
|
|
571
|
+
CGRP-005) prop_name="ManagedOOMMemoryPressure" ;;
|
|
572
|
+
CGRP-006) prop_name="ManagedOOMMemoryPressureLimit" ;;
|
|
573
|
+
CGRP-007) prop_name="CPUWeight" ;;
|
|
574
|
+
CGRP-008) prop_name="MemoryHigh" ;;
|
|
575
|
+
CGRP-009) prop_name="IOWeight" ;;
|
|
576
|
+
esac
|
|
577
|
+
|
|
578
|
+
local actual_value
|
|
579
|
+
actual_value=$(echo "$content" | grep "^${prop_name}=" 2>/dev/null | cut -d= -f2- || echo "")
|
|
580
|
+
|
|
581
|
+
if [[ -z "$actual_value" ]]; then
|
|
582
|
+
emit_fail "$id" "$parameter" "$after_value" "property_not_found"
|
|
583
|
+
elif [[ "$actual_value" == "$after_value" ]]; then
|
|
584
|
+
emit_pass "$id" "$parameter" "$after_value" "$actual_value"
|
|
585
|
+
else
|
|
586
|
+
emit_fail "$id" "$parameter" "$after_value" "$actual_value"
|
|
587
|
+
fi
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
# -------------------------------------------------------
|
|
591
|
+
# Check helper script entries (CGRP-010, CGRP-011)
|
|
592
|
+
# Check that OOMScoreAdjust value is in the script
|
|
593
|
+
# -------------------------------------------------------
|
|
594
|
+
check_helper_script() {
|
|
595
|
+
local id="$1" parameter="$2" config_file="$3" after_value="$4"
|
|
596
|
+
|
|
597
|
+
if [[ ! -f "$config_file" ]]; then
|
|
598
|
+
emit_fail "$id" "$parameter" "$after_value" "file_missing"
|
|
599
|
+
return
|
|
600
|
+
fi
|
|
601
|
+
|
|
602
|
+
local content
|
|
603
|
+
content=$(cat "$config_file" 2>/dev/null || echo "")
|
|
604
|
+
|
|
605
|
+
if echo "$content" | grep -q "OOMScoreAdjust=$after_value"; then
|
|
606
|
+
emit_pass "$id" "$parameter" "$after_value" "$after_value"
|
|
607
|
+
else
|
|
608
|
+
local found
|
|
609
|
+
found=$(echo "$content" | grep -oE 'OOMScoreAdjust=-?[0-9]+' | head -1 | cut -d= -f2 || echo "not found")
|
|
610
|
+
emit_fail "$id" "$parameter" "$after_value" "$found"
|
|
611
|
+
fi
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
# -------------------------------------------------------
|
|
615
|
+
# Main: iterate over all manifest entries
|
|
616
|
+
# -------------------------------------------------------
|
|
617
|
+
|
|
618
|
+
# Extract all entries as tab-separated values in one jq call
|
|
619
|
+
tsv_data=$(jq -r '.tuning_entries[] | [.id, .category, .parameter, .config_file, .before_value, .after_value] | @tsv' "$MANIFEST")
|
|
620
|
+
|
|
621
|
+
while IFS=$'\t' read -r id category parameter config_file before_value after_value; do
|
|
622
|
+
case "$config_file" in
|
|
623
|
+
/etc/sysctl.d/99-performance.conf)
|
|
624
|
+
# sysctl entries: read live value from /proc/sys
|
|
625
|
+
check_sysctl "$id" "$parameter" "$after_value" "$config_file"
|
|
626
|
+
;;
|
|
627
|
+
|
|
628
|
+
"systemctl disable/mask")
|
|
629
|
+
# Service disable/mask entries
|
|
630
|
+
check_service_disabled "$id" "$parameter"
|
|
631
|
+
;;
|
|
632
|
+
|
|
633
|
+
"systemctl")
|
|
634
|
+
# Service active check (CPU-003 irqbalance)
|
|
635
|
+
check_service_active "$id" "$parameter" "$after_value"
|
|
636
|
+
;;
|
|
637
|
+
|
|
638
|
+
/etc/fstab)
|
|
639
|
+
check_fstab_entry "$id" "$parameter" "$after_value"
|
|
640
|
+
;;
|
|
641
|
+
|
|
642
|
+
/etc/udev/rules.d/*)
|
|
643
|
+
check_udev_entry "$id" "$parameter" "$config_file" "$after_value"
|
|
644
|
+
;;
|
|
645
|
+
|
|
646
|
+
/etc/modules-load.d/*)
|
|
647
|
+
check_modules_load "$id" "$parameter" "$config_file" "$after_value"
|
|
648
|
+
;;
|
|
649
|
+
|
|
650
|
+
/etc/default/grub)
|
|
651
|
+
check_grub_entry "$id" "$parameter" "$after_value"
|
|
652
|
+
;;
|
|
653
|
+
|
|
654
|
+
/etc/tmpfiles.d/thp.conf)
|
|
655
|
+
check_thp_entry "$id" "$parameter" "$after_value"
|
|
656
|
+
;;
|
|
657
|
+
|
|
658
|
+
/etc/tmpfiles.d/ksm.conf)
|
|
659
|
+
check_ksm_entry "$id" "$parameter" "$after_value"
|
|
660
|
+
;;
|
|
661
|
+
|
|
662
|
+
/etc/environment)
|
|
663
|
+
check_environment_entry "$id" "$parameter" "$after_value"
|
|
664
|
+
;;
|
|
665
|
+
|
|
666
|
+
/etc/systemd/system/*.slice)
|
|
667
|
+
check_slice_entry "$id" "$parameter" "$config_file" "$after_value"
|
|
668
|
+
;;
|
|
669
|
+
|
|
670
|
+
/usr/local/bin/*)
|
|
671
|
+
check_helper_script "$id" "$parameter" "$config_file" "$after_value"
|
|
672
|
+
;;
|
|
673
|
+
|
|
674
|
+
*)
|
|
675
|
+
emit_fail "$id" "$parameter" "$after_value" "unknown_config_type:$config_file"
|
|
676
|
+
;;
|
|
677
|
+
esac
|
|
678
|
+
done <<< "$tsv_data"
|
|
679
|
+
|
|
680
|
+
# -------------------------------------------------------
|
|
681
|
+
# Summary
|
|
682
|
+
# -------------------------------------------------------
|
|
683
|
+
echo ""
|
|
684
|
+
echo "${PASS_COUNT}/${TOTAL} checks passed"
|
|
685
|
+
|
|
686
|
+
if [[ $FAIL_COUNT -eq 0 && $SKIP_COUNT -eq 0 ]]; then
|
|
687
|
+
exit 0
|
|
688
|
+
elif [[ $FAIL_COUNT -gt 0 ]]; then
|
|
689
|
+
exit 1
|
|
690
|
+
else
|
|
691
|
+
# Only SKIPs, no FAILs — still consider success
|
|
692
|
+
exit 0
|
|
693
|
+
fi
|