AgentScheduler.php 4.42 KiB
<?php
namespace App;
use App\Jobs\RunAgent;
use App\Sensor\Heartbeat;
use App\Sensor\StatusChangeDetector;
use Illuminate\Support\Facades\File;
use Illuminate\Support\LazyCollection;
use Illuminate\Support\Collection;
use Symfony\Component\Finder\SplFileInfo;
/**
* Implements sensor auto discovery and registration.
*
* Implements singleton pattern.
*
* @author tibo
*/
class AgentScheduler
{
/**
*
* @var LazyCollection<Sensor>
*/
private $sensors;
// associative array
// trigger_label => array<Sensor>
private array $triggers;
private function __construct()
{
$this->sensors = $this->autodiscover();
$this->triggers = $this->register($this->sensors);
}
private static $instance;
public static function get() : AgentScheduler
{
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
/**
*
* @return LazyCollection<Sensor>
*/
public function sensors() : LazyCollection
{
return $this->sensors;
}
/**
*
* @return LazyCollection<Sensor>
*/
public function autodiscover() : LazyCollection
{
$ROOT = __DIR__ . "/Sensor/";
return LazyCollection::make(File::allFiles($ROOT))->map(function (SplFileInfo $file) {
$interface_name = "\App\Sensor";
$class_name = '\App\Sensor\\' . $file->getFilenameWithoutExtension();
if (!is_a($class_name, $interface_name, true)) {
return;
}
$reflection = new \ReflectionClass($class_name);
if ($reflection->isAbstract()) {
return;
}
return new $class_name;
})->filter();
}
/**
*
* @param LazyCollection<Sensor> $sensors
* @return array
*/
public function register(LazyCollection $sensors) : array
{
$triggers = [];
foreach ($sensors as $sensor) {
/** @var Sensor $sensor */
$conf = $sensor->config();
$trigger_label = $conf->trigger_label;
$triggers[$trigger_label][] = $sensor;
}
return $triggers;
}
/**
* Get the list of defined agent labels.
*
* @return array
*/
public function agentLabel() : array
{
return $this->sensors->map(function (Sensor $sensor) {
return $sensor->config()->label;
})->toArray();
}
// ------------------ SCHEDULING of agents
public function notify(Record $record)
{
$trigger_label = $record->label;
if (! isset($this->triggers[$trigger_label])) {
return;
}
foreach ($this->triggers[$trigger_label] as $agent) {
/** @var Sensor $agent */
RunAgent::dispatch($agent, $record);
}
// special one : trigger heartbeat
$agent = new Heartbeat();
RunAgent::dispatch($agent, $record);
}
public function notifySummary(ReportSummary $summary)
{
(new StatusChangeDetector())->analyze($summary);
}
public function notifyStatusChange(StatusChange $change)
{
(new Sensor\ChangeAlert())->analyze($change);
}
public function notifyReport(Report $report)
{
$server = $report->server;
$reports = $this->lastReportsOf($server);
$summary = new ReportSummary();
$summary->time = time();
$summary->server_id = $server->id;
$summary->setReports($reports);
$summary->status_code = Status::max($reports)->code();
$summary->save();
}
/**
* Get the last report for each label.
*
* @return Collection<Report> last report for each label
*/
public function lastReportsOf(Server $server) : Collection
{
$reports = new Collection();
foreach ($this->agentLabel() as $label) {
$reports->push($this->lastReportOf($server, $label));
}
return $reports->filter();
}
public function lastReportOf(Server $server, string $label) : ?Report
{
$start = time() - 24 * 3600;
return $server->reports()
->where("label", $label)
->where("time", ">", $start)
->orderByDesc("id")
->first();
}
}