'boolean', 'threshold' => 'float', // % metrics are whole, but `load` is fractional (see the migration) ]; /** Metrics a rule can watch. `offline` fires when a server can't be polled (status offline). */ public const METRICS = ['cpu', 'mem', 'disk', 'load', 'offline']; public const COMPARATORS = ['gt', 'lt']; public const SCOPES = ['all', 'group', 'server']; protected static function booted(): void { static::creating(fn (self $r) => $r->uuid ??= (string) Str::uuid()); } public function getRouteKeyName(): string { return 'uuid'; } public function incidents(): HasMany { return $this->hasMany(AlertIncident::class); } /** Whether this rule applies to the given server (all / a group's members / one server). */ public function targets(Server $server): bool { return match ($this->scope_type) { 'server' => (int) $this->scope_id === $server->id, 'group' => $server->groups()->where('server_groups.id', $this->scope_id)->exists(), default => true, // 'all' }; } /** * Does the reading breach this rule? For numeric metrics, compare against the threshold with the * rule's comparator; for `offline`, the breach is simply that the server is not online. * * @param array $metrics cpu/mem/disk/load ints; status when offline */ public function breached(array $metrics, string $status): bool { if ($this->metric === 'offline') { return $status === 'offline'; // only a truly-down server, not a reachable "warning" } $value = (float) ($metrics[$this->metric] ?? 0); $threshold = (float) $this->threshold; return $this->comparator === 'lt' ? $value < $threshold : $value > $threshold; } /** The reading to record on the incident (the breached value; 0 for offline). Float for `load`. */ public function reading(array $metrics): float { return $this->metric === 'offline' ? 0.0 : (float) ($metrics[$this->metric] ?? 0); } }