78 lines
2.5 KiB
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);
|
|
}
|
|
}
|