diff --git a/README.md b/README.md index 744df29..77023ea 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,7 @@ Run a read-only health snapshot from the Debian server with: ./jeannie help ./jeannie status ./jeannie capacity +./jeannie capacity-advisor ./jeannie recover-plan ./jeannie recover-power --dry-run ./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 `--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 checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and edge verification. diff --git a/README.md.tmpl b/README.md.tmpl index 26f4cd0..7b73191 100644 --- a/README.md.tmpl +++ b/README.md.tmpl @@ -272,6 +272,7 @@ Run a read-only health snapshot from the Debian server with: ./{{ main_script }} help ./{{ main_script }} status ./{{ main_script }} capacity +./{{ main_script }} capacity-advisor ./{{ main_script }} recover-plan ./{{ main_script }} recover-power --dry-run ./{{ 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 `--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 checks, from Debian and Gitea through DNS, Pimox, Kubernetes, GitOps apps, and edge verification. diff --git a/docs/jeannie.1.md b/docs/jeannie.1.md index ff3ad55..b1580dd 100644 --- a/docs/jeannie.1.md +++ b/docs/jeannie.1.md @@ -66,6 +66,11 @@ deployment readiness, pod restarts, node pressure, disk pressure, Traefik Pimox, RPi, and placement signals. Use `capacity --details`, `--json`, `--only 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` : Print the ordered disaster recovery checklist and lightweight prerequisite checks for Debian, Gitea, RPi DNS, Pimox, Kubernetes, GitOps apps, and edge diff --git a/jeannie b/jeannie index 9a9024d..0beabe0 100755 --- a/jeannie +++ b/jeannie @@ -6011,6 +6011,11 @@ resource_budget() { "${REPO_ROOT}/scripts/resource-budget" "${@:2}" } +capacity_advisor() { + require_debian_server "capacity-advisor" + "${REPO_ROOT}/scripts/capacity-advisor" "${@:2}" +} + control_plane() { require_debian_server "control-plane" "${REPO_ROOT}/scripts/control-plane" "${@:2}" @@ -6127,6 +6132,7 @@ Operator View And Reports status Cascading health check from host to public URLs. scorecard Compact pass/warn/fail operator scorecard. 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-power [--dry-run] Run or preview post-outage recovery. impact [--since REF] [PATH...] Explain affected lab areas before changes. @@ -6227,6 +6233,9 @@ case "${1:-}" in capacity) capacity_report "$@" ;; + capacity-advisor) + capacity_advisor "$@" + ;; recover-plan) recover_plan ;; diff --git a/scripts/capacity-advisor b/scripts/capacity-advisor new file mode 100755 index 0000000..9f6c1b3 --- /dev/null +++ b/scripts/capacity-advisor @@ -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