Add compute_rollup() to capacity-scan.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
07ecbb2789
commit
b240fa8bfe
2 changed files with 67 additions and 0 deletions
|
|
@ -43,3 +43,38 @@ def parse_table(markdown, required_cols):
|
|||
rows.append(dict(zip(headers, cells)))
|
||||
return rows
|
||||
return []
|
||||
|
||||
|
||||
def compute_rollup(node_rows, workload_rows):
|
||||
"""Per node: physical totals, summed allocations, RAM headroom %, and an
|
||||
oversubscribed flag. Workloads on unknown nodes are ignored."""
|
||||
nodes = {}
|
||||
for r in node_rows:
|
||||
nodes[r["node"]] = {
|
||||
"cores": int(r["cores"]),
|
||||
"ram_gb": float(r["ram_gb"]),
|
||||
"disk_gb": float(r["disk_gb"]),
|
||||
"alloc_cores": 0,
|
||||
"alloc_ram_mb": 0,
|
||||
"alloc_disk_gb": 0.0,
|
||||
}
|
||||
for w in workload_rows:
|
||||
node = nodes.get(w["node"])
|
||||
if node is None:
|
||||
continue
|
||||
node["alloc_cores"] += int(w["cores"])
|
||||
node["alloc_ram_mb"] += int(w["ram_mb"])
|
||||
node["alloc_disk_gb"] += float(w["disk_gb"])
|
||||
for node in nodes.values():
|
||||
node["alloc_ram_gb"] = round(node.pop("alloc_ram_mb") / 1024, 1)
|
||||
node["ram_headroom_pct"] = (
|
||||
round(100 * (node["ram_gb"] - node["alloc_ram_gb"]) / node["ram_gb"])
|
||||
if node["ram_gb"]
|
||||
else 0
|
||||
)
|
||||
node["oversubscribed"] = (
|
||||
node["alloc_cores"] > node["cores"]
|
||||
or node["alloc_ram_gb"] > node["ram_gb"]
|
||||
or node["alloc_disk_gb"] > node["disk_gb"]
|
||||
)
|
||||
return nodes
|
||||
|
|
|
|||
|
|
@ -26,3 +26,35 @@ trailing text
|
|||
|
||||
def test_parse_table_returns_empty_when_header_absent():
|
||||
assert cs.parse_table("no tables here", ["node", "cores"]) == []
|
||||
|
||||
|
||||
def test_compute_rollup_sums_allocations_and_flags_headroom():
|
||||
node_rows = [{"node": "pve0", "cores": "20", "ram_gb": "64", "disk_gb": "4000"}]
|
||||
workload_rows = [
|
||||
{"workload": "dns1", "node": "pve0", "cores": "1", "ram_mb": "512", "disk_gb": "10"},
|
||||
{"workload": "forgejo", "node": "pve0", "cores": "4", "ram_mb": "8192", "disk_gb": "100"},
|
||||
]
|
||||
nodes = cs.compute_rollup(node_rows, workload_rows)
|
||||
pve0 = nodes["pve0"]
|
||||
assert pve0["alloc_cores"] == 5
|
||||
assert pve0["alloc_ram_gb"] == 8.5 # (512 + 8192) / 1024
|
||||
assert pve0["alloc_disk_gb"] == 110.0
|
||||
assert pve0["ram_headroom_pct"] == 87 # round(100 * (64 - 8.5) / 64)
|
||||
assert pve0["oversubscribed"] is False
|
||||
|
||||
|
||||
def test_compute_rollup_flags_oversubscription():
|
||||
node_rows = [{"node": "tiny", "cores": "2", "ram_gb": "4", "disk_gb": "50"}]
|
||||
workload_rows = [
|
||||
{"workload": "hog", "node": "tiny", "cores": "4", "ram_mb": "1024", "disk_gb": "10"},
|
||||
]
|
||||
nodes = cs.compute_rollup(node_rows, workload_rows)
|
||||
assert nodes["tiny"]["oversubscribed"] is True # 4 cores > 2
|
||||
|
||||
|
||||
def test_compute_rollup_ignores_workloads_on_unknown_nodes():
|
||||
nodes = cs.compute_rollup(
|
||||
[{"node": "pve0", "cores": "20", "ram_gb": "64", "disk_gb": "4000"}],
|
||||
[{"workload": "ghost", "node": "nope", "cores": "1", "ram_mb": "512", "disk_gb": "10"}],
|
||||
)
|
||||
assert nodes["pve0"]["alloc_cores"] == 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue