Add Jeannie capacity advisor

This commit is contained in:
juvdiaz 2026-06-29 20:46:15 -06:00
parent 31723307d5
commit eabc5b049b
5 changed files with 121 additions and 0 deletions

View File

@ -272,6 +272,7 @@ Run a read-only health snapshot from the Debian server with:
./jeannie help ./jeannie help
./jeannie status ./jeannie status
./jeannie capacity ./jeannie capacity
./jeannie capacity-advisor
./jeannie recover-plan ./jeannie recover-plan
./jeannie recover-power --dry-run ./jeannie recover-power --dry-run
./jeannie impact --since HEAD~1 ./jeannie impact --since HEAD~1
@ -323,6 +324,10 @@ Kubernetes, Pimox, RPi, and placement signals without dumping full details. Use
`./jeannie capacity --details`, `--json`, `--only problems`, or `./jeannie capacity --details`, `--json`, `--only problems`, or
`--verbose` for the older full capacity dump. `--verbose` for the older full capacity dump.
`capacity-advisor` interprets capacity, resource-budget, and control-plane
placement signals to recommend whether to add Pimox VMs, taint Debian, or fix
resource definitions first.
`recover-plan` prints the disaster recovery order and lightweight prerequisite `recover-plan` prints the disaster recovery order and lightweight prerequisite
checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and
edge verification. edge verification.

View File

@ -272,6 +272,7 @@ Run a read-only health snapshot from the Debian server with:
./{{ main_script }} help ./{{ main_script }} help
./{{ main_script }} status ./{{ main_script }} status
./{{ main_script }} capacity ./{{ main_script }} capacity
./{{ main_script }} capacity-advisor
./{{ main_script }} recover-plan ./{{ main_script }} recover-plan
./{{ main_script }} recover-power --dry-run ./{{ main_script }} recover-power --dry-run
./{{ main_script }} impact --since HEAD~1 ./{{ main_script }} impact --since HEAD~1
@ -323,6 +324,10 @@ Kubernetes, Pimox, RPi, and placement signals without dumping full details. Use
`./{{ main_script }} capacity --details`, `--json`, `--only problems`, or `./{{ main_script }} capacity --details`, `--json`, `--only problems`, or
`--verbose` for the older full capacity dump. `--verbose` for the older full capacity dump.
`capacity-advisor` interprets capacity, resource-budget, and control-plane
placement signals to recommend whether to add Pimox VMs, taint Debian, or fix
resource definitions first.
`recover-plan` prints the disaster recovery order and lightweight prerequisite `recover-plan` prints the disaster recovery order and lightweight prerequisite
checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and
edge verification. edge verification.

View File

@ -66,6 +66,11 @@ deployment readiness, pod restarts, node pressure, disk pressure, Traefik
Pimox, RPi, and placement signals. Use `capacity --details`, `--json`, `--only Pimox, RPi, and placement signals. Use `capacity --details`, `--json`, `--only
problems`, or `--verbose` for the older full dump. problems`, or `--verbose` for the older full dump.
`capacity-advisor`
: Interpret capacity, resource-budget, and control-plane placement signals to
recommend whether to add Pimox VMs, taint Debian, or fix resource definitions
first.
`recover-plan` `recover-plan`
: Print the ordered disaster recovery checklist and lightweight prerequisite : Print the ordered disaster recovery checklist and lightweight prerequisite
checks for Debian, Gitea, RPi DNS, Pimox, Kubernetes, GitOps apps, and edge checks for Debian, Gitea, RPi DNS, Pimox, Kubernetes, GitOps apps, and edge

View File

@ -6011,6 +6011,11 @@ resource_budget() {
"${REPO_ROOT}/scripts/resource-budget" "${@:2}" "${REPO_ROOT}/scripts/resource-budget" "${@:2}"
} }
capacity_advisor() {
require_debian_server "capacity-advisor"
"${REPO_ROOT}/scripts/capacity-advisor" "${@:2}"
}
control_plane() { control_plane() {
require_debian_server "control-plane" require_debian_server "control-plane"
"${REPO_ROOT}/scripts/control-plane" "${@:2}" "${REPO_ROOT}/scripts/control-plane" "${@:2}"
@ -6127,6 +6132,7 @@ Operator View And Reports
status Cascading health check from host to public URLs. status Cascading health check from host to public URLs.
scorecard Compact pass/warn/fail operator scorecard. scorecard Compact pass/warn/fail operator scorecard.
capacity Compact capacity and placement report. capacity Compact capacity and placement report.
capacity-advisor Decide whether to add VMs or fix placement/resources.
recover-plan Print disaster recovery order and prerequisites. recover-plan Print disaster recovery order and prerequisites.
recover-power [--dry-run] Run or preview post-outage recovery. recover-power [--dry-run] Run or preview post-outage recovery.
impact [--since REF] [PATH...] Explain affected lab areas before changes. impact [--since REF] [PATH...] Explain affected lab areas before changes.
@ -6227,6 +6233,9 @@ case "${1:-}" in
capacity) capacity)
capacity_report "$@" capacity_report "$@"
;; ;;
capacity-advisor)
capacity_advisor "$@"
;;
recover-plan) recover-plan)
recover_plan recover_plan
;; ;;

97
scripts/capacity-advisor Executable file
View File

@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
run_check() {
local name="$1"
shift
echo "## ${name}"
set +e
"$@" 2>&1
local status=$?
set -e
echo "exit_status=${status}"
}
tmp_file="$(mktemp "${TMPDIR:-/tmp}/jeannie-capacity-advisor.XXXXXX")"
trap 'rm -f "$tmp_file"' EXIT
{
run_check "capacity" "${REPO_ROOT}/scripts/capacity-report" --only problems
run_check "resource-budget" "${REPO_ROOT}/scripts/resource-budget"
run_check "control-plane" "${REPO_ROOT}/scripts/control-plane" status
} >"$tmp_file"
python3 - "$tmp_file" <<'PY'
import re
import sys
text = open(sys.argv[1], encoding="utf-8").read()
lowered = text.lower()
missing_match = re.search(r"(\d+)\s+pods missing requests/limits", text)
missing_count = int(missing_match.group(1)) if missing_match else 0
pressure = any(token in lowered for token in ["diskpressure", "memorypressure", "node pressure"])
worker_storage_warn = "worker storage" in lowered and "warn" in lowered
non_system_control_plane = False
control_plane_section = text.split("## control-plane", 1)[-1]
for line in control_plane_section.splitlines():
if re.match(r"\s*(argocd|website-production|demos-static|heimdall|arr-stack|security-lab|monitoring)\s+", line):
non_system_control_plane = True
break
print("Jeannie Capacity Advisor")
print("========================")
recommendations = []
if missing_count:
recommendations.append(
(
"Fix resource requests/limits first",
f"{missing_count} pods are missing requests/limits, so scheduling and capacity math are not trustworthy yet.",
"./jeannie resource-budget --details",
"code/config: add resources to app manifests or Helm values",
)
)
if non_system_control_plane:
recommendations.append(
(
"Keep app/data-plane pods off Debian",
"Non-system workloads are still scheduled on the Debian control-plane host.",
"./jeannie control-plane taint",
"scheduling: taint Debian after Pimox workers are Ready",
)
)
if pressure or worker_storage_warn:
recommendations.append(
(
"Consider adding or recreating Pimox worker capacity",
"Cluster pressure or worker storage pressure is visible after current placement checks.",
"./jeannie workers list",
"capacity: increase LAB_PIMOX_WORKER_COUNT or recreate the pressured worker",
)
)
if not recommendations:
recommendations.append(
(
"No new Pimox VM needed from current signals",
"No capacity warning, node pressure, or control-plane placement issue was detected in the sampled reports.",
"./jeannie capacity",
"watch: rerun after adding workloads",
)
)
for index, (title, why, command, fix) in enumerate(recommendations, start=1):
print(f"\n{index}. {title}")
print(f" Why: {why}")
print(f" Next command: {command}")
print(f" Fix path: {fix}")
print("\nRule of thumb")
print("- Add requests before deciding you need more VMs.")
print("- Taint Debian once Pimox workers are stable.")
print("- Add a Pimox VM only when real pressure remains after placement and requests are cleaned up.")
PY