Skip to content
Snippets Groups Projects
Server.php 8.17 KiB
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;

class Server extends Model
{

    protected $fillable = ["token"];

    /**
     * Last record from this server (used for caching).
     * @var String
     */
    private $last_record = null;

    private static $sensors = [
        \App\Sensor\LoadAvg::class,
        \App\Sensor\MemInfo::class,
        \App\Sensor\Ifconfig::class,
        \App\Sensor\Netstat::class,
        \App\Sensor\ListeningPorts::class,
        \App\Sensor\Reboot::class,
        \App\Sensor\Updates::class,
        \App\Sensor\Disks::class,
        \App\Sensor\Inodes::class,
        \App\Sensor\Ssacli::class,
        \App\Sensor\Perccli::class,
        \App\Sensor\Date::class,
        \App\Sensor\ClientVersion::class,
        \App\Sensor\Heartbeat::class,
        \App\Sensor\DiskEvolution::class
    ];

    public function __construct(array $attributes = array())
    {
        $attributes["token"] = str_random(32);
        parent::__construct($attributes);
    }

    public function organization()
    {
        return $this->belongsTo("App\Organization");
    }

    public function lastRecord()
    {
        if ($this->last_record == null) {
            $collection = \Mongo::get()->monitoring->records;
            $this->last_record =  $collection->findOne(
                ["server_id" => $this->id],
                ["sort" => ["_id" => -1]]
            );
        }

        return $this->last_record;
    }

    public function hasData() : bool
    {
        return $this->lastRecord() != null;
    }

    /**
     * Return the last record if it contains the field $field,
     * otherwise return null.
     * @param string $field
     * @return string
     */
    public function lastRecordContaining(string $field)
    {
        if (isset($this->lastRecord()->$field)) {
            return $this->lastRecord();
        }

        return null;
    }

    /**
     *
     * @return \Carbon\Carbon
     */
    public function lastRecordTime()
    {
        $last_record = $this->lastRecord();
        if ($last_record === null) {
            return \Carbon\Carbon::createFromTimestamp(0);
        }

        return \Carbon\Carbon::createFromTimestamp($last_record->time);
    }

    public function clientVersion()
    {
        $last_record = $this->lastRecord();
        if ($last_record == null) {
            return "none";
        }

        return $last_record->version;
    }

    public function lastClientUrl()
    {
        $client_sensor = new \App\Sensor\ClientVersion($this);
        return $client_sensor->latestUrl();
    }

    /**
     * Get integer status of server.
     * @return int
     */
    public function status()
    {
        return max($this->statusArray());
    }

    public function statusBadge()
    {
        return AbstractSensor::getBadgeForStatus($this->status());
    }

    public function statusArray()
    {
        $status_array = [];
        foreach ($this->getSensors() as $sensor) {
            $sensor_name = \get_class($sensor);
            try {
                $status_array[$sensor_name] = $sensor->status();
            } catch (\Exception $ex) {
                $status_array[$sensor_name] = Sensor::STATUS_UNKNOWN;
                Log::error("Sensor $sensor_name failed : " . $ex->getTraceAsString());
            }
        }
        return $status_array;
    }

    public function getSensorsNOK()
    {
        $sensorsNOK = [];
        foreach ($this->getSensors() as $sensor) {
            if ($sensor->status() > 0) {
                $sensorsNOK[] = $sensor;
            }
        }
        return $sensorsNOK;
    }

    public function statusString()
    {
        return self::getNameForStatus($this->status());
    }

    public static function getNameForStatus($status)
    {
        switch ($status) {
            case 0:
                return "OK";
            case 10:
                return "WARNING";
            case 20:
                return "ERROR";
            default:
                return "Unknown";
        }
    }

    public function getBadge()
    {
        return AbstractSensor::getBadgeForStatus($this->status());
    }

    public function color()
    {
        return AbstractSensor::getColorForStatus($this->status());
    }

    public function getSensors()
    {

        $sensors = [];
        foreach (self::$sensors as $sensor) {
            $sensors[] = new $sensor($this);
        }
        return $sensors;
    }

    public function uptime()
    {
        $record = $this->lastRecordContaining("upaimte");
        if ($record == null) {
            return "";
        }

        return $this->parseUptime($record->upaimte);
    }

    public function parseUptime(string $string)
    {
        $pieces = explode(' ', $string);
        $uptime = \Carbon\Carbon::now()->subSeconds($pieces[0]);
        return $uptime->diffForHumans(null, true);
    }

    public function uuid()
    {
        $record = $this->lastRecordContaining("system");
        if ($record == null) {
            return "";
        }

        return $this->parseUUID($record->system);
    }

    const UUID = "/\s*UUID: (.*)/m";

    public function parseUUID(string $string) : string
    {
        $matches = array();
        preg_match(self::UUID, $string, $matches);
        if (! isset($matches[1])) {
            return "unknown";
        }
        return $matches[1];
    }


    public function cpuinfo()
    {
        $record = $this->lastRecordContaining("cpu");
        if ($record == null) {
            return null;
        }

        return $this->parseCpuinfo($record->cpu);
    }

    const CPU_INFO = "/^model name	: (.+)$/m";
    public function parseCpuinfo($string)
    {
        $matches = array();
        preg_match_all(self::CPU_INFO, $string, $matches);

        $result["threads"] = count($matches[0]);
        $result["cpu"] = $matches[1][0];
        return $result;
    }

    public function meminfo()
    {
        return round($this->memoryTotal() / 1000 / 1000) . " GB";
    }

    /**
     *
     * @return int total memory (in KB)
     */
    public function memoryTotal()
    {
        $record = $this->lastRecordContaining("memory");
        if ($record == null) {
            return null;
        }

        return $this->parseMeminfo($record->memory);
    }

    const MEMINFO = "/^MemTotal:\\s+([0-9]+) kB$/m";
    public function parseMeminfo($string)
    {
        $matches = array();
        preg_match(self::MEMINFO, $string, $matches);
        $total = $matches[1];
        return $total;
    }

    public function lsb()
    {

        $record = $this->lastRecordContaining("lsb");
        if ($record == null) {
            return "";
        }

        return $this->parseLsb($record->lsb);
    }

    const LSB = "/^Description:	(.+)$/m";
    public function parseLsb($string)
    {
        $matches = [];
        preg_match(self::LSB, $string, $matches);
        return $matches[1];
    }



    const REGEX_MANUFACTURER = "/^\s*Manufacturer: (.*)$/m";
    public function parseManufacturer(string $string) : string
    {
        $matches = [];
        preg_match(self::REGEX_MANUFACTURER, $string, $matches);

        if (!isset($matches[1])) {
            return "unkwnown";
        }
        return $matches[1];
    }

    public function manufacturer()
    {
        $record = $this->lastRecordContaining("system");
        if ($record == null) {
            return "Unknown";
        }

        return $this->parseManufacturer($record->system);
    }

    const REGEX_PRODUCT_NAME = "/^\s*Product Name: (.*)$/m";
    public function parseProductName(string $string) : string
    {
        $matches = [];
        preg_match(self::REGEX_PRODUCT_NAME, $string, $matches);
        if (!isset($matches[1])) {
            return "unkwnown";
        }
        return $matches[1];
    }

    public function productName()
    {
        $record = $this->lastRecordContaining("system");
        if ($record == null) {
            return "";
        }

        return $this->parseProductName($record->system);
    }

    public function getChanges($count = 10)
    {
        return \App\StatusChange::getLastChangesForServer($this->id, $count);
    }

    public static function id($id) : Server
    {
        return self::where("id", $id)->first();
    }
}