clusev/app/Models/AlertRule.php

78 lines
2.5 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
/**
* A threshold rule over a server metric. When a targeted server crosses it, the AlertEvaluator
* opens a firing AlertIncident and notifies; when it recovers, the incident resolves.
*/
class AlertRule extends Model
{
protected $guarded = [];
protected $casts = [
'enabled' => '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<string, int|string> $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);
}
}