148 lines
3.6 KiB
Python
148 lines
3.6 KiB
Python
from metrics.target_scoring import TargetScorer
|
|
from metrics.target_labeler import TargetLabeler
|
|
from metrics.profile_store import save
|
|
|
|
|
|
class LiveTargetState:
|
|
|
|
def __init__(self, target_key):
|
|
self.target = target_key
|
|
|
|
# -----------------------------
|
|
# RUNNING STATE (incremental)
|
|
# -----------------------------
|
|
self.cpu_sum = 0
|
|
self.temp_sum = 0
|
|
self.samples = 0
|
|
|
|
self.peak_temp = 0
|
|
|
|
self.actions = 0
|
|
self.cache_hits = 0
|
|
self.failures = 0
|
|
|
|
# -----------------------------
|
|
# CACHED DERIVED VALUES
|
|
# -----------------------------
|
|
self.cached_score = None
|
|
self.cached_labels = None
|
|
self.dirty = True
|
|
|
|
|
|
class FesterIntelligence:
|
|
|
|
def __init__(self):
|
|
|
|
self.states = {}
|
|
self.scorer = TargetScorer()
|
|
self.labeler = TargetLabeler()
|
|
|
|
# -----------------------------
|
|
# INIT
|
|
# -----------------------------
|
|
def start_target(self, target_key):
|
|
|
|
self.states[target_key] = LiveTargetState(target_key)
|
|
|
|
# -----------------------------
|
|
# FAST UPDATES (O(1))
|
|
# -----------------------------
|
|
def sample(self, target_key, node):
|
|
|
|
s = self.states[target_key]
|
|
|
|
cpu = node.get("cpu_load", 0)
|
|
temp = node.get("temp", 0)
|
|
|
|
s.cpu_sum += cpu
|
|
s.temp_sum += temp
|
|
s.samples += 1
|
|
|
|
if temp > s.peak_temp:
|
|
s.peak_temp = temp
|
|
|
|
s.dirty = True
|
|
|
|
def record_event(self, target_key, event):
|
|
|
|
s = self.states[target_key]
|
|
|
|
if event == "action":
|
|
s.actions += 1
|
|
|
|
elif event == "cache_hit":
|
|
s.cache_hits += 1
|
|
|
|
elif event == "failure":
|
|
s.failures += 1
|
|
|
|
s.dirty = True
|
|
|
|
# -----------------------------
|
|
# FAST SNAPSHOT (NO RECOMPUTE UNLESS DIRTY)
|
|
# -----------------------------
|
|
def snapshot(self, target_key):
|
|
|
|
s = self.states.get(target_key)
|
|
|
|
if not s:
|
|
return None
|
|
|
|
# -----------------------------
|
|
# RETURN CACHED RESULT
|
|
# -----------------------------
|
|
if not s.dirty and s.cached_score is not None:
|
|
return {
|
|
"score": s.cached_score,
|
|
"labels": s.cached_labels
|
|
}
|
|
|
|
# -----------------------------
|
|
# COMPUTE AGGREGATES
|
|
# -----------------------------
|
|
avg_cpu = s.cpu_sum / s.samples if s.samples else 0
|
|
avg_temp = s.temp_sum / s.samples if s.samples else 0
|
|
|
|
cache_ratio = s.cache_hits / s.actions if s.actions else 0
|
|
|
|
profile = {
|
|
"duration": 0, # intentionally not scheduler-relevant anymore
|
|
"avg_cpu": avg_cpu,
|
|
"avg_temp": avg_temp,
|
|
"peak_temp": s.peak_temp,
|
|
"cache_ratio": cache_ratio,
|
|
"failures": s.failures
|
|
}
|
|
|
|
score = self.scorer.score(profile)
|
|
labels = self.labeler.label(score)
|
|
|
|
# -----------------------------
|
|
# CACHE RESULT
|
|
# -----------------------------
|
|
s.cached_score = score
|
|
s.cached_labels = labels
|
|
s.dirty = False
|
|
|
|
return {
|
|
"score": score,
|
|
"labels": labels
|
|
}
|
|
|
|
# -----------------------------
|
|
# FINALIZE (ARCHIVAL ONLY)
|
|
# -----------------------------
|
|
def finalize(self, target_key):
|
|
|
|
s = self.states[target_key]
|
|
|
|
snapshot = self.snapshot(target_key)
|
|
|
|
save({
|
|
"target": target_key,
|
|
"score": snapshot["score"],
|
|
"labels": snapshot["labels"]
|
|
})
|
|
|
|
return snapshot
|