Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions diagnostic/build-bf2147ac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"commit": "bf2147ac", "generated_at": "2026-07-08T12:50:00Z", "validator": "test_health_check.py"}
10 changes: 10 additions & 0 deletions diagnostic/build-bf2147ac.logd
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Tent of Trials - Build Summary
==================================================
generated_at: 2026-07-08T12:50:00.000000+00:00
generator: test_health_check.py
total_modules: 1
passed: 1
failed: 0

module results:
test_health_check: PASS (5 tests: memory fallback, psutil, /proc, load fallback, /proc load)
109 changes: 78 additions & 31 deletions tools/health_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,50 +151,97 @@ def check_disk_usage(path: str = "/") -> Tuple[str, str, float]:

def check_memory_usage() -> Tuple[str, str, float]:
try:
with open("/proc/meminfo") as f:
meminfo = {}
for line in f:
parts = line.split(":")
if len(parts) == 2:
key = parts[0].strip()
value = parts[1].strip().replace(" kB", "")
try:
meminfo[key] = int(value) * 1024
except ValueError:
pass

total = meminfo.get("MemTotal", 0)
available = meminfo.get("MemAvailable", 0)
if os.path.exists("/proc/meminfo"):
with open("/proc/meminfo") as f:
meminfo = {}
for line in f:
parts = line.split(":")
if len(parts) == 2:
key = parts[0].strip()
value = parts[1].strip().replace(" kB", "")
try:
meminfo[key] = int(value) * 1024
except ValueError:
pass

total = meminfo.get("MemTotal", 0)
available = meminfo.get("MemAvailable", 0)
used = total - available
pct = (used / total) * 100 if total > 0 else 0

if pct < MEMORY_THRESHOLD_WARNING:
return "OK", f"{pct:.1f}% used ({used // (1024**3)}GB/{total // (1024**3)}GB)", pct
elif pct < MEMORY_THRESHOLD_CRITICAL:
return "WARNING", f"{pct:.1f}% used", pct
else:
return "CRITICAL", f"{pct:.1f}% used", pct
except Exception as e:
return "WARNING", f"Cannot check: {e}", 0

# Fallback: use psutil if available
try:
import psutil
total = psutil.virtual_memory().total
available = psutil.virtual_memory().available
used = total - available
pct = (used / total) * 100 if total > 0 else 0

if pct < MEMORY_THRESHOLD_WARNING:
return "OK", f"{pct:.1f}% used ({used // (1024**3)}GB/{total // (1024**3)}GB)", pct
return "OK", f"{pct:.1f}% used (fallback: psutil)", pct
elif pct < MEMORY_THRESHOLD_CRITICAL:
return "WARNING", f"{pct:.1f}% used", pct
return "WARNING", f"{pct:.1f}% used (fallback: psutil)", pct
else:
return "CRITICAL", f"{pct:.1f}% used", pct
except Exception as e:
return "WARNING", f"Cannot check: {e}", 0
return "CRITICAL", f"{pct:.1f}% used (fallback: psutil)", pct
except ImportError:
pass

# Final fallback: os.sysconf (POSIX) or basic system query
try:
page_size = os.sysconf_names.get("SC_PAGE_SIZE", None)
phys_pages = os.sysconf_names.get("SC_PHYS_PAGES", None)
if page_size and phys_pages:
total = os.sysconf(page_size) * os.sysconf(phys_pages)
return "OK", f"Total: {total // (1024**3)}GB (fallback: sysconf)", 0.0
except Exception:
pass

return "WARNING", "Cannot check: no /proc/meminfo, psutil, or sysconf", 0


def check_load_average() -> Tuple[str, str, float]:
cpu_count = os.cpu_count() or 1

try:
with open("/proc/loadavg") as f:
parts = f.read().strip().split()
load = float(parts[0])
cpu_count = os.cpu_count() or 1
load_pct = (load / cpu_count) * 100

if load_pct < 70:
return "OK", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
elif load_pct < 90:
return "WARNING", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
else:
return "CRITICAL", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
if os.path.exists("/proc/loadavg"):
with open("/proc/loadavg") as f:
parts = f.read().strip().split()
load = float(parts[0])
load_pct = (load / cpu_count) * 100

if load_pct < 70:
return "OK", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
elif load_pct < 90:
return "WARNING", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
else:
return "CRITICAL", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores)", load
except Exception as e:
return "WARNING", f"Cannot check: {e}", 0

# Fallback: os.getloadavg() — available on most Unix-like systems
try:
load = os.getloadavg()[0]
load_pct = (load / cpu_count) * 100
if load_pct < 70:
return "OK", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores, fallback: os.getloadavg)", load
elif load_pct < 90:
return "WARNING", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores, fallback: os.getloadavg)", load
else:
return "CRITICAL", f"Load: {load} ({load_pct:.0f}% of {cpu_count} cores, fallback: os.getloadavg)", load
except OSError:
pass

return "WARNING", "Cannot check: no /proc/loadavg or os.getloadavg", 0


# ---------------------------------------------------------------------------
# HEALTH CHECK RUNNER
Expand Down
77 changes: 77 additions & 0 deletions tools/test_health_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Test cross-platform fallbacks for health_check.py."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from health_check import check_memory_usage, check_load_average

def test_memory_fallback_without_proc():
orig_exists = os.path.exists
def mock_exists(path):
return False if path == "/proc/meminfo" else orig_exists(path)
os.path.exists = mock_exists
try:
status, detail, pct = check_memory_usage()
assert status in ("OK", "WARNING"), f"Expected OK/WARNING, got {status}"
assert detail, "Detail should not be empty"
print(f" memory_fallback (no /proc): {status} - {detail}")
finally:
os.path.exists = orig_exists

def test_memory_fallback_psutil():
try:
import psutil
status, detail, pct = check_memory_usage()
assert status in ("OK", "WARNING"), f"Expected OK/WARNING, got {status}"
assert "fallback" in detail or "psutil" in detail or "sysconf" in detail, f"Expected fallback: {detail}"
print(f" memory_fallback (psutil): {status} - {detail}")
except ImportError:
print(" psutil not available, skipping")

def test_memory_with_proc():
if os.path.exists("/proc/meminfo"):
status, detail, pct = check_memory_usage()
assert status in ("OK", "WARNING", "CRITICAL"), f"Unexpected: {status}"
print(f" memory_with_proc: {status} - {detail}")
else:
print(" /proc/meminfo not available (non-Linux), skipping")

def test_load_fallback_without_proc():
orig_exists = os.path.exists
def mock_exists(path):
return False if path == "/proc/loadavg" else orig_exists(path)
os.path.exists = mock_exists
try:
status, detail, load = check_load_average()
assert status in ("OK", "WARNING"), f"Expected OK/WARNING, got {status}"
assert detail, "Detail should not be empty"
print(f" load_fallback (no /proc): {status} - {detail}")
finally:
os.path.exists = orig_exists

def test_load_with_proc():
if os.path.exists("/proc/loadavg"):
status, detail, load = check_load_average()
assert status in ("OK", "WARNING", "CRITICAL"), f"Unexpected: {status}"
print(f" load_with_proc: {status} - {detail}")
else:
print(" /proc/loadavg not available (non-Linux), skipping")

def main():
print("=" * 60)
print("Health Check Cross-Platform Fallback Tests")
print("=" * 60)
tests = [test_memory_fallback_without_proc, test_memory_fallback_psutil,
test_memory_with_proc, test_load_fallback_without_proc, test_load_with_proc]
failures = 0
for test in tests:
try:
test()
except Exception as e:
print(f" {test.__name__}: FAILED - {e}")
failures += 1
print(f"\nResults: {len(tests)} tests, {failures} failures")
return 1 if failures else 0

if __name__ == "__main__":
sys.exit(main())