From b1f8009e60d4a283c3166c7e8eac681b3a8910f2 Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Fri, 24 Apr 2026 10:43:13 -0700 Subject: [PATCH 1/2] source purchasedNodes from Facility instead of facilityRecentComputeUsage --- modules/coact.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/coact.py b/modules/coact.py index 1b7a72b..d20b3bf 100644 --- a/modules/coact.py +++ b/modules/coact.py @@ -964,7 +964,7 @@ def get(self, date: str) -> Iterator[OveragePoint]: def get_data(self) -> dict: """Fetch usage data from GraphQL.""" per_window_template = Template( - """_$key: facilityRecentComputeUsage(pastMinutes:$minutes) { cluster: clustername, facility, percentUsed, purchasedNodes }""" + """_$key: facilityRecentComputeUsage(pastMinutes:$minutes) { cluster: clustername, facility, percentUsed }""" ) logger.trace(f"Fetching windows {self.windows}") all_windows = [] @@ -974,6 +974,7 @@ def get_data(self) -> dict: query = "query usage {" query += "\n".join(all_windows) + ",\n" + query += "facilities(filter:{}) { name, computepurchases { clustername, purchased } },\n" query += "repos { facility, allocs: currentComputeAllocations { cluster: clustername, start, end } }" query += "\n}" @@ -984,6 +985,12 @@ def get_data(self) -> dict: def format_data(self, result: dict) -> dict: """Format the raw data for processing.""" + # Build purchased nodes lookup from Facility.computepurchases + fac_purchases = {} + for fac in result.pop("facilities", []): + for cp in fac.get("computepurchases") or []: + fac_purchases[(fac["name"].lower(), cp["clustername"].lower())] = cp["purchased"] + current = {} for k in result["repos"]: f = k["facility"].lower() @@ -991,7 +998,7 @@ def format_data(self, result: dict) -> dict: current[f] = {} for item in k["allocs"]: c = item["cluster"].lower() - current[f][c] = {"held": None, "percentUsed": [], "purchasedNodes": None} + current[f][c] = {"held": None, "percentUsed": [], "purchasedNodes": fac_purchases.get((f, c))} del result["repos"] for time, array in result.items(): @@ -999,11 +1006,8 @@ def format_data(self, result: dict) -> dict: for a in array: f = a["facility"].lower() c = a["cluster"].lower() - logger.trace(f"Setting {f} {c} to {a['percentUsed']} (nodes: {a.get('purchasedNodes')})") + logger.trace(f"Setting {f} {c} to {a['percentUsed']}") current[f][c]["percentUsed"].append(int(a["percentUsed"])) - # Store purchased nodes (use the value from any time window since it's constant) - if a.get("purchasedNodes") is not None and current[f][c]["purchasedNodes"] is None: - current[f][c]["purchasedNodes"] = a["purchasedNodes"] logger.trace(f"Overages: {current}") From 650966cc351002c1fb5a0fea7d562eabc3ad0fb8 Mon Sep 17 00:00:00 2001 From: Ryan Waldheim Date: Fri, 24 Apr 2026 10:45:26 -0700 Subject: [PATCH 2/2] update unit tests --- tests/test_node_allocation.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_node_allocation.py b/tests/test_node_allocation.py index d9ca0cf..c12bc0f 100644 --- a/tests/test_node_allocation.py +++ b/tests/test_node_allocation.py @@ -20,8 +20,11 @@ def create_graphql_response(usage_percent: float, nodes: int): ] } ], + "facilities": [ + {"name": "LCLS", "computepurchases": [{"clustername": "ada", "purchased": nodes}]} + ], "000060": [ - {"facility": "LCLS", "cluster": "ada", "percentUsed": usage_percent, "purchasedNodes": nodes}, + {"facility": "LCLS", "cluster": "ada", "percentUsed": usage_percent}, ] } @@ -51,7 +54,7 @@ def test_facility_lifecycle_goes_over_blocks_recovers_and_restores_nodes(): dry_run=False ) - # GraphQL response includes purchasedNodes from coact-api + # GraphQL response includes purchasedNodes from Facility.computepurchases graphql_response = { "repos": [ { @@ -61,8 +64,11 @@ def test_facility_lifecycle_goes_over_blocks_recovers_and_restores_nodes(): ] } ], + "facilities": [ + {"name": "LCLS", "computepurchases": [{"clustername": "ada", "purchased": purchased_nodes}]} + ], "000060": [ - {"facility": "LCLS", "cluster": "ada", "percentUsed": 85, "purchasedNodes": purchased_nodes}, + {"facility": "LCLS", "cluster": "ada", "percentUsed": 85}, ] }