Skip to content
Snippets Groups Projects
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();
    }
}