feat: dynamic summary, --hosts filter, and --json output (#24)
All checks were successful
Auto-merge docs-only PRs / auto-merge-docs (pull_request) Successful in 2s
All checks were successful
Auto-merge docs-only PRs / auto-merge-docs (pull_request) Successful in 2s
Closes #24 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a97f443f60
commit
2918081ab2
@ -30,6 +30,9 @@ MEM_WARN=85
|
|||||||
ZOMBIE_WARN=1
|
ZOMBIE_WARN=1
|
||||||
SWAP_WARN=512
|
SWAP_WARN=512
|
||||||
|
|
||||||
|
HOSTS_FILTER="" # comma-separated host list from --hosts; empty = audit all
|
||||||
|
JSON_OUTPUT=0 # set to 1 by --json
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--output-dir)
|
--output-dir)
|
||||||
@ -40,6 +43,18 @@ while [[ $# -gt 0 ]]; do
|
|||||||
REPORT_DIR="$2"
|
REPORT_DIR="$2"
|
||||||
shift 2
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
--hosts)
|
||||||
|
if [[ $# -lt 2 ]]; then
|
||||||
|
echo "Error: --hosts requires an argument" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
HOSTS_FILTER="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--json)
|
||||||
|
JSON_OUTPUT=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1" >&2
|
echo "Unknown option: $1" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@ -50,6 +65,7 @@ done
|
|||||||
mkdir -p "$REPORT_DIR"
|
mkdir -p "$REPORT_DIR"
|
||||||
SSH_FAILURES_LOG="$REPORT_DIR/ssh-failures.log"
|
SSH_FAILURES_LOG="$REPORT_DIR/ssh-failures.log"
|
||||||
FINDINGS_FILE="$REPORT_DIR/findings.txt"
|
FINDINGS_FILE="$REPORT_DIR/findings.txt"
|
||||||
|
AUDITED_HOSTS=() # populated in main; used by generate_summary for per-host counts
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Remote collector script
|
# Remote collector script
|
||||||
@ -281,6 +297,18 @@ generate_summary() {
|
|||||||
printf " Critical : %d\n" "$crit_count"
|
printf " Critical : %d\n" "$crit_count"
|
||||||
echo "=============================="
|
echo "=============================="
|
||||||
|
|
||||||
|
if [[ ${#AUDITED_HOSTS[@]} -gt 0 ]] && ((warn_count + crit_count > 0)); then
|
||||||
|
echo ""
|
||||||
|
printf " %-30s %8s %8s\n" "Host" "Warnings" "Critical"
|
||||||
|
printf " %-30s %8s %8s\n" "----" "--------" "--------"
|
||||||
|
for host in "${AUDITED_HOSTS[@]}"; do
|
||||||
|
local hw hc
|
||||||
|
hw=$(grep -c "^WARN ${host}:" "$FINDINGS_FILE" 2>/dev/null || true)
|
||||||
|
hc=$(grep -c "^CRIT ${host}:" "$FINDINGS_FILE" 2>/dev/null || true)
|
||||||
|
((hw + hc > 0)) && printf " %-30s %8d %8d\n" "$host" "$hw" "$hc"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
if ((warn_count + crit_count > 0)); then
|
if ((warn_count + crit_count > 0)); then
|
||||||
echo ""
|
echo ""
|
||||||
echo "Findings:"
|
echo "Findings:"
|
||||||
@ -293,6 +321,9 @@ generate_summary() {
|
|||||||
grep '^SSH_FAILURE' "$SSH_FAILURES_LOG" | awk '{print " " $2 " (" $3 ")"}'
|
grep '^SSH_FAILURE' "$SSH_FAILURES_LOG" | awk '{print " " $2 " (" $3 ")"}'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
printf "Total: %d warning(s), %d critical across %d host(s)\n" \
|
||||||
|
"$warn_count" "$crit_count" "$host_count"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Reports: $REPORT_DIR"
|
echo "Reports: $REPORT_DIR"
|
||||||
}
|
}
|
||||||
@ -383,6 +414,69 @@ check_cert_expiry() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# JSON report — writes findings.json to $REPORT_DIR when --json is used
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
write_json_report() {
|
||||||
|
local host_count="$1"
|
||||||
|
local json_file="$REPORT_DIR/findings.json"
|
||||||
|
local ssh_failure_count=0
|
||||||
|
local warn_count=0
|
||||||
|
local crit_count=0
|
||||||
|
|
||||||
|
[[ -f "$SSH_FAILURES_LOG" ]] &&
|
||||||
|
ssh_failure_count=$(grep -c '^SSH_FAILURE' "$SSH_FAILURES_LOG" 2>/dev/null || true)
|
||||||
|
[[ -f "$FINDINGS_FILE" ]] &&
|
||||||
|
warn_count=$(grep -c '^WARN' "$FINDINGS_FILE" 2>/dev/null || true)
|
||||||
|
[[ -f "$FINDINGS_FILE" ]] &&
|
||||||
|
crit_count=$(grep -c '^CRIT' "$FINDINGS_FILE" 2>/dev/null || true)
|
||||||
|
|
||||||
|
python3 - "$json_file" "$host_count" "$ssh_failure_count" \
|
||||||
|
"$warn_count" "$crit_count" "$FINDINGS_FILE" <<'PYEOF'
|
||||||
|
import sys, json, datetime
|
||||||
|
|
||||||
|
json_file = sys.argv[1]
|
||||||
|
host_count = int(sys.argv[2])
|
||||||
|
ssh_failure_count = int(sys.argv[3])
|
||||||
|
warn_count = int(sys.argv[4])
|
||||||
|
crit_count = int(sys.argv[5])
|
||||||
|
findings_file = sys.argv[6]
|
||||||
|
|
||||||
|
findings = []
|
||||||
|
try:
|
||||||
|
with open(findings_file) as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
parts = line.split(None, 2)
|
||||||
|
if len(parts) < 3:
|
||||||
|
continue
|
||||||
|
severity, host_colon, message = parts[0], parts[1], parts[2]
|
||||||
|
findings.append({
|
||||||
|
"severity": severity,
|
||||||
|
"host": host_colon.rstrip(":"),
|
||||||
|
"message": message,
|
||||||
|
})
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"timestamp": datetime.datetime.utcnow().isoformat() + "Z",
|
||||||
|
"hosts_audited": host_count,
|
||||||
|
"warnings": warn_count,
|
||||||
|
"critical": crit_count,
|
||||||
|
"ssh_failures": ssh_failure_count,
|
||||||
|
"total_findings": warn_count + crit_count,
|
||||||
|
"findings": findings,
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(json_file, "w") as f:
|
||||||
|
json.dump(output, f, indent=2)
|
||||||
|
print(f"JSON report: {json_file}")
|
||||||
|
PYEOF
|
||||||
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Main
|
# Main
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@ -390,22 +484,46 @@ main() {
|
|||||||
echo "Starting homelab audit — $(date)"
|
echo "Starting homelab audit — $(date)"
|
||||||
echo "Report dir: $REPORT_DIR"
|
echo "Report dir: $REPORT_DIR"
|
||||||
echo "STUCK_PROC_CPU_WARN threshold: ${STUCK_PROC_CPU_WARN}%"
|
echo "STUCK_PROC_CPU_WARN threshold: ${STUCK_PROC_CPU_WARN}%"
|
||||||
|
[[ -n "$HOSTS_FILTER" ]] && echo "Host filter: $HOSTS_FILTER"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
>"$FINDINGS_FILE"
|
>"$FINDINGS_FILE"
|
||||||
|
|
||||||
echo " Checking Proxmox backup recency..."
|
|
||||||
check_backup_recency
|
|
||||||
|
|
||||||
local host_count=0
|
local host_count=0
|
||||||
while read -r label addr; do
|
|
||||||
echo " Auditing $label ($addr)..."
|
if [[ -n "$HOSTS_FILTER" ]]; then
|
||||||
parse_and_report "$label" "$addr"
|
# --hosts mode: audit specified hosts directly, skip Proxmox inventory
|
||||||
check_cert_expiry "$label" "$addr"
|
local check_proxmox=0
|
||||||
((host_count++)) || true
|
IFS=',' read -ra filter_hosts <<<"$HOSTS_FILTER"
|
||||||
done < <(collect_inventory)
|
for host in "${filter_hosts[@]}"; do
|
||||||
|
[[ "$host" == "proxmox" ]] && check_proxmox=1
|
||||||
|
done
|
||||||
|
if ((check_proxmox)); then
|
||||||
|
echo " Checking Proxmox backup recency..."
|
||||||
|
check_backup_recency
|
||||||
|
fi
|
||||||
|
for host in "${filter_hosts[@]}"; do
|
||||||
|
echo " Auditing $host..."
|
||||||
|
parse_and_report "$host" "$host"
|
||||||
|
check_cert_expiry "$host" "$host"
|
||||||
|
AUDITED_HOSTS+=("$host")
|
||||||
|
((host_count++)) || true
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo " Checking Proxmox backup recency..."
|
||||||
|
check_backup_recency
|
||||||
|
|
||||||
|
while read -r label addr; do
|
||||||
|
echo " Auditing $label ($addr)..."
|
||||||
|
parse_and_report "$label" "$addr"
|
||||||
|
check_cert_expiry "$label" "$addr"
|
||||||
|
AUDITED_HOSTS+=("$label")
|
||||||
|
((host_count++)) || true
|
||||||
|
done < <(collect_inventory)
|
||||||
|
fi
|
||||||
|
|
||||||
generate_summary "$host_count"
|
generate_summary "$host_count"
|
||||||
|
[[ "$JSON_OUTPUT" -eq 1 ]] && write_json_report "$host_count"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user