Skip to content
Snippets Groups Projects
Commit 4c3f41fd authored by Tibo's avatar Tibo
Browse files

major refactor detection agents

parent 4f0b3774
No related branches found
No related tags found
No related merge requests found
Pipeline #12864 passed
Showing
with 440 additions and 510 deletions
<?php
namespace App;
/**
*
* @author tibo
*/
interface HasStatus
{
public function status() : Status;
}
<?php
namespace App;
/**
* Status report
*
* @author tibo
*/
class Report implements HasStatus
{
private $name;
private $status;
private $html = "";
/**
* Name is mandatory.
* Default status is "Unknown"
*/
public function __construct(string $name, ?Status $status = null, ?string $html = "")
{
$this->name = $name;
$this->status = $status;
$this->html = $html;
if ($status == null) {
$this->status = Status::unknown();
} else {
$this->status = $status;
}
}
public function setStatus(Status $status) : Report
{
$this->status = $status;
return $this;
}
public function setHTML(string $html) : Report
{
$this->html = $html;
return $this;
}
public function name() : string
{
return $this->name;
}
public function status() : Status
{
return $this->status;
}
public function html() : string
{
return $this->html;
}
}
...@@ -2,44 +2,14 @@ ...@@ -2,44 +2,14 @@
namespace App; namespace App;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Base (abstract) class for sensors. * Sensors must analyze a collection of Record, and produce a Report.
* *
* @author tibo * @author tibo
*/ */
abstract class Sensor interface Sensor
{ {
public function analyze(Collection $records, ServerInfo $serverinfo) : Report;
private $server;
public function __construct(?Server $server = null)
{
$this->server = $server;
}
protected function server() : Server
{
return $this->server;
}
/**
* Get the name of the sensor. Can be overridden by sub-classes to provide
* a more meaningful name.
*
* @return string
*/
public function name() : string
{
return (new \ReflectionClass($this))->getShortName();
}
/**
* Compute the status code from an array of Record.
*/
abstract public function status(array $records) : int;
/**
* Create the HTML report describing the result of this sensor's analysis.
*/
abstract public function report(array $records) : string;
} }
...@@ -2,145 +2,71 @@ ...@@ -2,145 +2,71 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of Update * Description of Update
* *
* @author helha * @author helha
*/ */
class CPUtemperature extends Sensor class CPUtemperature implements Sensor
{ {
// Match a CPU line like
const REGEXP = "/^(Core \d+):\s+\+(\d+\.\d+)/m"; // Package id 0: +39.0°C (high = +84.0°C, crit = +100.0°C)
const REGEXPCPU= "/^(Package id)+\s+(\d):\s+\+(\d+\.\d+)°C\s+\(high\s=\s\+\d+\.\d°C,\scrit\s=\s\+(\d+\.\d+)°C\)/m"; const REGEXPCPU= "/^(Package id)+\s+(\d):\s+\+(\d+\.\d+)°C\s+\(high\s=\s\+\d+\.\d°C,\scrit\s=\s\+(\d+\.\d+)°C\)/m";
// Mach a core line
// Core 0: +38.0°C (high = +84.0°C, crit = +100.0°C)
const REGEXPCORE = "/^(Core \d+):\s+\+(\d+\.\d+)°C\s+\(high\s=\s\+\d+\.\d°C,\scrit\s=\s\+(\d+\.\d+)°C\)/m";
public function report(array $records) : string
public function analyze(Collection $records, ServerInfo $serverinfo): Report
{ {
$record = end($records); $report = new Report("CPU temperature");
$record = $records->last();
if (! isset($record->data["cpu-temperature"])) { if (! isset($record->data["cpu-temperature"])) {
return "<p>No data available...</p>" return $report->setHTML("<p>No data available...</p>"
. "<p>Maybe <code>sensors</code> is not installed.</p>" . "<p>Maybe <code>sensors</code> is not installed.</p>"
. "<p>You can install it with <code>sudo apt install lm-sensors</code></p>"; . "<p>You can install it with <code>sudo apt install lm-sensors</code></p>");
} }
$Cores = self::parseCPUtemperature($record->data['cpu-temperature']);
$CPUS = self::parseCPU($record->data['cpu-temperature']);
$return = "<table class='table table-sm'>"; $cpus = $this->parse($record->data['cpu-temperature']);
$return .= "<tr><th>Name</th><th>Temperature (°C)</th><th>T°crit (°C)</th></tr>"; $report->setHTML(view("sensor.cputemperature", ["cpus" => $cpus]));
$report->setStatus(Status::max($cpus));
foreach ($CPUS as $CPU) { return $report;
$return .= "<tr><td>" . "<b>" ."CPU " . $CPU->number . "</td><td>"
. "<b>" . $CPU->value . "</td><td>" . "<b>" . $CPU->critvalue . "</td></tr>";
foreach ($Cores as $Core) {
if ($Core->number == $CPU->number) {
$return .= "<tr><td>" . $Core->name . "</td><td>"
. $Core->corevalue . "</td><td>" . " " . "</td></tr>";
}
}
}
$return .= "</table>";
return $return;
} }
public function status(array $records) : int public function parse(string $string)
{ {
$record = end($records); $cpus = [];
if (! isset($record->data["cpu-temperature"])) { $cpu = null;
return \App\Status::UNKNOWN;
} $lines = explode("\n", $string);
$all_status = [];
foreach (self::parseCPU($record->data['cpu-temperature']) as $CPU) {
/* @var $CPU Cpu */
$status = \App\Status::OK;
if ($CPU->value > $CPU->critvalue) {
$status = \App\Status::WARNING;
}
foreach (self::parseCPUtemperature($record->data['cpu-temperature']) as $Core) {
if ($Core->number == $CPU->number) {
if ($Core->value > $CPU->critvalue) {
$status = \App\Status::WARNING;
}
}
}
$all_status[] = $status;
}
if (count($all_status) < 1) {
return \App\Status::UNKNOWN;
}
return max($all_status);
}
public static function parse(string $string) //cores only
{
$values = array();
preg_match_all(self::REGEXP, $string, $values);
$temperatures = array();
$count = count($values[1]);
for ($i = 0; $i < $count; $i++) {
$CPUTemp = new Temperature();
$CPUTemp->name = $values[1][$i];
$CPUTemp->value = $values[2][$i];
$temperatures[] = $CPUTemp;
}
return $temperatures;
}
public static function parseCPU(string $string) //cpus only
{
$values = array();
preg_match_all(self::REGEXPCPU, $string, $values);
$CPUS = array();
$count = count($values[1]);
for ($i = 0; $i < $count; $i++) {
$CPU = new Cpu();
$CPU->number = $values[2][$i];
$CPU->value = $values[3][$i];
$CPU->critvalue = $values[4][$i];
$CPUS[] = $CPU;
}
return $CPUS;
}
public function parseCPUtemperature(string $string) //cores (to associate with cpus only in report() )
{
if ($string == null) {
return [];
}
$current_cpu = new Cpu();
$CPUS=[];
$Cores=[];
$lines=explode("\n", $string);
foreach ($lines as $line) { foreach ($lines as $line) {
$matchesCPU = array(); $match = [];
if (preg_match(self::REGEXPCPU, $line, $matchesCPU) === 1) {
$current_cpu = new Cpu(); // this line corresponds to a CPU definition
$current_cpu->number = $matchesCPU[2]; if (preg_match(self::REGEXPCPU, $line, $match) === 1) {
$CPUS[]=$current_cpu; $cpu = new Cpu($match[2], $match[3], $match[4]);
$cpus[] = $cpu;
continue; continue;
} }
$matchesCore = array();
if (preg_match(self::REGEXP, $line, $matchesCore) === 1) { // line correponds to a core definition
$Core=new Temperature(); if (preg_match(self::REGEXPCORE, $line, $match) === 1) {
$Core->name = $matchesCore[1]; $core = new Core($match[1], $match[2], $match[3]);
$Core->corevalue = $matchesCore[2]; // append to current CPU
$Core->number = $current_cpu->number; $cpu->cores[] = $core;
$Cores[] = $Core;
continue; continue;
} }
} }
return $Cores; return $cpus;
}
public function pregMatchOne($pattern, $string)
{
$matches = array();
if (preg_match($pattern, $string, $matches) === 1) {
return $matches[1];
}
return false;
} }
} }
...@@ -2,43 +2,51 @@ ...@@ -2,43 +2,51 @@
namespace App\Sensor; namespace App\Sensor;
use App\Sensor;
use App\Jobs\FetchClientManifest; use App\Jobs\FetchClientManifest;
use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Check if the latest version of the client is installed. * Check if the latest version of the client is installed.
* *
* @author tibo * @author tibo
*/ */
class ClientVersion extends Sensor class ClientVersion implements Sensor
{ {
public function report(array $records) : string public function analyze(Collection $records, ServerInfo $serverinfo): Report
{ {
return "<p>Installed version: " . $this->installedVersion($records) . "</p>" $latest_version = FetchClientManifest::version();
. "<p>Latest client version: " . FetchClientManifest::version() . "</p>"; $installed_version = $this->installedVersion($records);
}
$report = new Report("Client Version");
$report->setHTML(
"<p>Installed version: $installed_version</p>" .
"<p>Latest client version: $latest_version</p>"
);
if ($latest_version == null) {
$report->setStatus(Status::unknown());
} elseif ($installed_version === $latest_version) {
$report->setStatus(Status::ok());
} else {
$report->setStatus(Status::warning());
}
public function installedVersion(array $records) return $report;
}
public function installedVersion(Collection $records) : string
{ {
$last_record = end($records); $last_record = $records->last();
if ($last_record == null) { if ($last_record == null) {
return "none"; return "none";
} }
return $last_record->data["version"]; return $last_record->data["version"];
} }
public function status(array $records) : int
{
$latest_version = FetchClientManifest::version();
if ($latest_version == null) {
return \App\Status::UNKNOWN;
}
if ($this->installedVersion($records) === $latest_version) {
return \App\Status::OK;
}
return \App\Status::WARNING;
}
} }
<?php
namespace App\Sensor;
use App\Status;
use App\HasStatus;
/**
* Description of Temperature (core)
*
* @author helha
*/
class Core implements HasStatus
{
public $name = ""; // eg : core 0
public $value; // eg : 42.5
public $critvalue; // eg : 76.0
public function __construct(string $name, float $value, float $critvalue)
{
$this->name = $name;
$this->value = $value;
$this->critvalue = $critvalue;
}
public function status() : Status
{
if ($this->value >= $this->critvalue) {
return Status::warning();
}
return Status::ok();
}
}
...@@ -2,14 +2,23 @@ ...@@ -2,14 +2,23 @@
namespace App\Sensor; namespace App\Sensor;
use App\Status;
/** /**
* Description of Cpu * Description of Cpu
* *
* @author helha * @author helha
*/ */
class Cpu class Cpu extends Core
{ {
public $number = ""; //eg : 1,2,3,4,...
public $value= ""; //eg : 38.0 public $cores = [];
public $critvalue= ""; //eg : 76.0
public function status() : Status
{
return max(
Status::max($this->cores),
parent::status()
);
}
} }
...@@ -2,46 +2,37 @@ ...@@ -2,46 +2,37 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use App\Record;
use App\Status; use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of Reboot * Description of Reboot
* *
* @author tibo * @author tibo
*/ */
class Date extends Sensor class Date implements Sensor
{ {
public function report(array $records) : string public function analyze(Collection $records, ServerInfo $serverinfo): Report
{
return "<p>Time drift: " . $this->delta(end($records)) . " seconds</p>";
}
public function status(array $records) : int
{ {
if (count($records) == 0) { $report = new Report("Time drift");
return Status::UNKNOWN; /** @var \App\Record $last_record */
} $last_record = $records->last();
$delta = $this->delta(end($records)); if (! isset($last_record->data["date"])) {
if ($delta == null) { return $report->setHTML("<p>No data available ...</p>");
return Status::UNKNOWN;
} }
$delta = $last_record->data["date"] - $last_record->time;
$report->setHTML("<p>Time drift: $delta seconds</p>");
if (abs($delta) > 10) { if (abs($delta) > 10) {
return Status::WARNING; return $report->setStatus(Status::warning());
}
return Status::OK;
}
public function delta(Record $record)
{
if (! isset($record->data["date"])) {
return null;
} }
return $record->data["date"] - $record->time; return $report->setStatus(Status::ok());
} }
} }
...@@ -2,17 +2,30 @@ ...@@ -2,17 +2,30 @@
namespace App\Sensor; namespace App\Sensor;
use App\Status;
use App\HasStatus;
/** /**
* Description of Disk * Description of Disk
* *
* @author tibo * @author tibo
*/ */
class Disk class Disk implements HasStatus
{ {
public $port = ""; public $port = "";
public $box = 0; public $box = 0;
public $bay = 0; public $bay = 0;
public $type = ""; public $type = "";
public $size = ""; public $size = "";
public $status = "";
/**
*
* @var \App\Status
*/
public $status;
public function status() : Status
{
return $this->status;
}
} }
...@@ -2,54 +2,39 @@ ...@@ -2,54 +2,39 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use App\ServerInfo;
use App\Report;
use App\Status;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of Update * Description of Update
* *
* @author tibo * @author tibo
*/ */
class Disks extends Sensor class Disks implements Sensor
{ {
const REGEXP = "/\\n([A-z\/0-9:\\-\\.]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)%\s*([A-z\/0-9]+)/"; const REGEXP = "/\\n([A-z\/0-9:\\-\\.]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)%\s*([A-z\/0-9]+)/";
public function report(array $records) : string public function analyze(Collection $records, ServerInfo $serverinfo): Report
{ {
$record = end($records); $report = new Report("Partitions");
$record = $records->last();
if (! isset($record->data['disks'])) { if (! isset($record->data['disks'])) {
return "<p>No data available...</p>"; return $report->setHTML("<p>No data available...</p>");
} }
$partitions = self::parse($record->data["disks"]); $partitions = $this->parse($record->data["disks"]);
return view("sensor.disks", ["partitions" => $partitions]); $report->setHTML(view("sensor.disks", ["partitions" => $partitions]));
return $report->setStatus(Status::max($partitions));
} }
public function status(array $records) : int public function parse(string $string) : array
{
$record = end($records);
if (! isset($record->data['disks'])) {
return \App\Status::UNKNOWN;
}
$all_status = [];
foreach (self::parse($record->data["disks"]) as $partition) {
/* @var $partition Partition */
$status = \App\Status::OK;
if ($partition->usedPercent() > 80) {
$status = \App\Status::WARNING;
} elseif ($partition->usedPercent() > 95) {
$status = \App\Status::ERROR;
}
$all_status[] = $status;
}
return max($all_status);
}
public static $skip_fs = ["none", "tmpfs", "shm", "udev", "overlay", '/dev/loop'];
public static function parse(string $string) : array
{ {
$values = array(); $values = array();
preg_match_all(self::REGEXP, $string, $values); preg_match_all(self::REGEXP, $string, $values);
...@@ -57,7 +42,7 @@ class Disks extends Sensor ...@@ -57,7 +42,7 @@ class Disks extends Sensor
$count = count($values[1]); $count = count($values[1]);
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$fs = $values[1][$i]; $fs = $values[1][$i];
if (self::shouldSkip($fs)) { if ($this->shouldSkip($fs)) {
continue; continue;
} }
...@@ -71,9 +56,9 @@ class Disks extends Sensor ...@@ -71,9 +56,9 @@ class Disks extends Sensor
return $partitions; return $partitions;
} }
public static function fromRecord($record) : array public function fromRecord($record) : array
{ {
$partitions = self::parse($record->data["disks"]); $partitions = $this->parse($record->data["disks"]);
$time = $record->time; $time = $record->time;
foreach ($partitions as $partition) { foreach ($partitions as $partition) {
$partition->time = $time; $partition->time = $time;
...@@ -81,11 +66,13 @@ class Disks extends Sensor ...@@ -81,11 +66,13 @@ class Disks extends Sensor
return $partitions; return $partitions;
} }
public static function shouldSkip(string $fs) : bool const SKIP_FS = ["none", "tmpfs", "shm", "udev", "overlay", '/dev/loop'];
public function shouldSkip(string $fs) : bool
{ {
foreach (self::$skip_fs as $should_skip) { foreach (self::SKIP_FS as $should_skip) {
if (self::startsWith($should_skip, $fs)) { if ($this->startsWith($should_skip, $fs)) {
return true; return true;
} }
} }
...@@ -93,7 +80,7 @@ class Disks extends Sensor ...@@ -93,7 +80,7 @@ class Disks extends Sensor
return false; return false;
} }
public static function startsWith(string $needle, string $haystack) : bool public function startsWith(string $needle, string $haystack) : bool
{ {
return substr($haystack, 0, strlen($needle)) === $needle; return substr($haystack, 0, strlen($needle)) === $needle;
} }
......
...@@ -2,51 +2,36 @@ ...@@ -2,51 +2,36 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
use Carbon\Carbon;
/** /**
* Description of Reboot * Description of Reboot
* *
* @author tibo * @author tibo
*/ */
class Heartbeat extends Sensor class Heartbeat implements Sensor
{ {
//put your code here public function analyze(Collection $records, ServerInfo $serverinfo): Report
public function report(array $records) : string
{
return "<p>Last heartbeat received "
. $this->lastRecordTime(end($records))->diffForHumans() . "</p>";
}
/**
*
* @return \Carbon\Carbon
*/
public function lastRecordTime($record) : \Carbon\Carbon
{ {
if ($record === null) { $report = new Report("Heartbeat");
return \Carbon\Carbon::createFromTimestamp(0);
} $record = $records->last();
$report->setHTML("<p>Last heartbeat received "
return \Carbon\Carbon::createFromTimestamp($record->time); . Carbon::createFromTimestamp($record->time)->diffForHumans() . "</p>");
}
$delta = \time() - $record->time;
public function status(array $records) : int
{
$record = end($records);
if ($record === null) {
$delta = PHP_INT_MAX;
} else {
$delta = \time() - $record->time;
}
// > 15 minutes // > 15 minutes
if ($delta > 900) { if ($delta > 900) {
return \App\Status::ERROR; return $report->setStatus(Status::error());
} }
return \App\Status::OK; return $report->setStatus(Status::ok());
} }
} }
...@@ -2,29 +2,36 @@ ...@@ -2,29 +2,36 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use \App\Record; use App\Status;
use App\Record;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of MemInfo * Description of MemInfo
* *
* @author tibo * @author tibo
*/ */
class Ifconfig extends Sensor class Ifconfig implements Sensor
{ {
public function analyze(Collection $records, ServerInfo $serverinfo): Report
public function report(array $records) : string
{ {
$record = end($records); $report = new Report("Ifconfig");
if (! isset($record->data['ifconfig'])) {
return "<p>No data available...</p>"; $last_record = $records->last();
if (! isset($last_record->data['ifconfig'])) {
return $report->setHTML("<p>No data available...</p>");
} }
$interfaces = $this->parseIfconfigRecord($record); $interfaces = $this->parseIfconfigRecord($last_record);
return view("agent.ifconfig", ["interfaces" => $interfaces]); return $report->setStatus(Status::ok())
->setHTML(view("agent.ifconfig", ["interfaces" => $interfaces]));
} }
public function points(array $records) public function points(Collection $records)
{ {
// Compute the time ordered list of arrays of interfaces // Compute the time ordered list of arrays of interfaces
$interfaces = []; $interfaces = [];
...@@ -85,11 +92,6 @@ class Ifconfig extends Sensor ...@@ -85,11 +92,6 @@ class Ifconfig extends Sensor
return array_values($dataset); return array_values($dataset);
} }
public function status(array $records) : int
{
return \App\Status::OK;
}
const IFNAME = '/^(?|(\S+)\s+Link encap:|(\S+): flags)/m'; const IFNAME = '/^(?|(\S+)\s+Link encap:|(\S+): flags)/m';
const IPV4 = '/^\s+inet (?>addr:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/m'; const IPV4 = '/^\s+inet (?>addr:)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/m';
const RXTX = '/^\s+RX bytes:(\d+) .*TX bytes:(\d+)/m'; const RXTX = '/^\s+RX bytes:(\d+) .*TX bytes:(\d+)/m';
...@@ -114,7 +116,6 @@ class Ifconfig extends Sensor ...@@ -114,7 +116,6 @@ class Ifconfig extends Sensor
*/ */
public function parseIfconfig(string $string) : array public function parseIfconfig(string $string) : array
{ {
$allowed_prefixes = ["en", "eth", "wl", "venet"]; $allowed_prefixes = ["en", "eth", "wl", "venet"];
if ($string == null) { if ($string == null) {
......
...@@ -2,55 +2,37 @@ ...@@ -2,55 +2,37 @@
namespace App\Sensor; namespace App\Sensor;
use App\Sensor;
use App\ServerInfo;
use App\Report;
use App\Status;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of Update * Description of Update
* *
* @author tibo * @author tibo
*/ */
class Inodes extends \App\Sensor class Inodes implements Sensor
{ {
const REGEXP = "/\\n([A-z\/0-9:\\-\\.]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)%\s*([A-z\/0-9]+)/"; const REGEXP = "/\\n([A-z\/0-9:\\-\\.]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)\s*([0-9]+)%\s*([A-z\/0-9]+)/";
public function report(array $records) : string public function analyze(Collection $records, ServerInfo $serverinfo): Report
{ {
$record = end($records); $report = new Report("Inodes");
$record = $records->last();
if (! isset($record->data['inodes'])) { if (! isset($record->data['inodes'])) {
return "<p>No data available...</p>"; return $report->setHTML("<p>No data available...</p>");
} }
$disks = $this->parse($record->data["inodes"]); $disks = $this->parse($record->data["inodes"]);
$return = "<table class='table table-sm'>"; $report->setHTML(view("sensor.inodes", ["disks" => $disks]));
$return .= "<tr><th></th><th></th><th>Usage</th></tr>";
foreach ($disks as $disk) { return $report->setStatus(Status::max($disks));
$return .= "<tr><td>" . $disk->filesystem . "</td><td>"
. $disk->mounted . "</td><td>" . $disk->usedPercent()
. "%</td></tr>";
}
$return .= "</table>";
return $return;
}
public function status(array $records) : int
{
$record = end($records);
if (! isset($record->data['inodes'])) {
return \App\Status::UNKNOWN;
}
$all_status = [];
foreach ($this->parse($record->data["inodes"]) as $disk) {
/* @var $disk InodesDisk */
$status = \App\Status::OK;
if ($disk->usedPercent() > 80) {
$status = \App\Status::WARNING;
} elseif ($disk->usedPercent() > 95) {
$status = \App\Status::ERROR;
}
$all_status[] = $status;
}
return max($all_status);
} }
public function parse(string $string) public function parse(string $string)
...@@ -59,9 +41,13 @@ class Inodes extends \App\Sensor ...@@ -59,9 +41,13 @@ class Inodes extends \App\Sensor
preg_match_all(self::REGEXP, $string, $values); preg_match_all(self::REGEXP, $string, $values);
$disks = array(); $disks = array();
$count = count($values[1]); $count = count($values[1]);
$disks_sensor = new Disks();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$fs = $values[1][$i]; $fs = $values[1][$i];
if (Disks::shouldSkip($fs)) {
if ($disks_sensor->shouldSkip($fs)) {
continue; continue;
} }
......
<?php <?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace App\Sensor; namespace App\Sensor;
use App\Status;
use App\HasStatus;
/** /**
* Description of InodesDisk * Description of InodesDisk
* *
* @author tibo * @author tibo
*/ */
class InodesDisk class InodesDisk implements HasStatus
{ {
public $filesystem = ""; public $filesystem = "";
public $inodes = 0; public $inodes = 0;
...@@ -24,4 +21,17 @@ class InodesDisk ...@@ -24,4 +21,17 @@ class InodesDisk
{ {
return round(100.0 * $this->used / $this->inodes); return round(100.0 * $this->used / $this->inodes);
} }
public function status() : Status
{
if ($this->usedPercent() > 95) {
return Status::error();
}
if ($this->usedPercent() > 80) {
return Status::warning();
}
return Status::ok();
}
} }
...@@ -2,24 +2,32 @@ ...@@ -2,24 +2,32 @@
namespace App\Sensor; namespace App\Sensor;
use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Parse the output of netstat to list listening ports. * Parse the output of netstat to list listening ports.
* *
* @author tibo * @author tibo
*/ */
class ListeningPorts extends \App\Sensor class ListeningPorts implements Sensor
{ {
const REGEXP = "/(tcp6|tcp|udp6|udp)\s*\d\s*\d\s*(\S*):(\d*).*LISTEN\s*(\S*)/m"; const REGEXP = "/(tcp6|tcp|udp6|udp)\s*\d\s*\d\s*(\S*):(\d*).*LISTEN\s*(\S*)/m";
public function report(array $records) : string public function analyze(Collection $records, ServerInfo $serverinfo): Report
{ {
$record = end($records); $report = new Report("Listening Ports");
$record = $records->last();
// "netstat-listen-tcp" "netstat-listen-udp" // "netstat-listen-tcp" "netstat-listen-udp"
if (! isset($record->data["netstat-listen-udp"]) if (! isset($record->data["netstat-listen-udp"])
&& ! isset($record->data["netstat-listen-tcp"])) { && ! isset($record->data["netstat-listen-tcp"])) {
return "<p>No data available...</p>"; return $report->setHTML("<p>No data available...</p>");
} }
$ports = array_merge( $ports = array_merge(
...@@ -33,29 +41,9 @@ class ListeningPorts extends \App\Sensor ...@@ -33,29 +41,9 @@ class ListeningPorts extends \App\Sensor
return $port1->port - $port2->port; return $port1->port - $port2->port;
} }
); );
$return = "<table class='table table-sm'>"; return $report->setStatus(Status::ok())
$return .= "<tr>" ->setHTML(view("sensor.listeningports", ["ports" => $ports]));
. "<th>Port</th>"
. "<th>Proto</th>"
. "<th>Bind address</th>"
. "<th>Process</th>"
. "</tr>";
foreach ($ports as $port) {
$return .= "<tr>"
. "<td>" . $port->port . "</td>"
. "<td>" . $port->proto . "</td>"
. "<td>" . $port->bind . "</td>"
. "<td>" . $port->process . "</td>"
. "</tr>";
}
$return .= "</table>";
return $return;
}
public function status(array $records) : int
{
return \App\Status::OK;
} }
/** /**
......
...@@ -2,37 +2,52 @@ ...@@ -2,37 +2,52 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Record;
use \App\Status; use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of LoadAvg * Description of LoadAvg
* *
* @author tibo * @author tibo
*/ */
class LoadAvg extends Sensor class LoadAvg implements Sensor
{ {
/** public function analyze(Collection $records, ServerInfo $serverinfo): Report
*
* @param array<\App\Record> $records
* @return string
*/
public function report(array $records) : string
{ {
$record = end($records); $threshold = $serverinfo->cpuinfo()["threads"];
if (! isset($record->data['loadavg'])) { $report = new Report("Load Average");
return "<p>No data available...</p>";
if (! isset($records->last()->data['loadavg'])) {
return $report->setHTML("<p>No data available...</p>");
}
$current_load = $this->parse($records->last()->data["loadavg"]);
$report->setHTML(view("agent.loadavg", ["current_load" => $current_load]));
$max_load = $records
->map(function (Record $record) {
$this->parse($record->data["loadavg"]);
})
->max();
if ($max_load > 2 * $threshold) {
return $report->setStatus(Status::error());
} }
$current_load = $this->parse($record->data["loadavg"]);
return view( if ($max_load > $threshold) {
"agent.loadavg", return $report->setStatus(Status::warning());
["current_load" => $current_load] }
);
return $report->setStatus(Status::ok());
} }
public function loadPoints(array $records) public function loadPoints(Collection $records)
{ {
$points = []; $points = [];
foreach ($records as $record) { foreach ($records as $record) {
...@@ -44,29 +59,6 @@ class LoadAvg extends Sensor ...@@ -44,29 +59,6 @@ class LoadAvg extends Sensor
return $points; return $points;
} }
public function status(array $records) : int
{
$threshold = $this->server()->info()->cpuinfo()["threads"];
$max = 0;
foreach ($records as $record) {
$load = $this->parse($record->data["loadavg"]);
if ($load > $max) {
$max = $load;
}
}
if ($max > 2 * $threshold) {
return Status::ERROR;
}
if ($max > $threshold) {
return Status::WARNING;
}
return Status::OK;
}
public function parse(string $string) : string public function parse(string $string) : string
{ {
return current(explode(" ", $string)); return current(explode(" ", $string));
......
...@@ -2,22 +2,37 @@ ...@@ -2,22 +2,37 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Description of MemInfo * Description of MemInfo
* *
* @author tibo * @author tibo
*/ */
class MemInfo extends Sensor class MemInfo implements Sensor
{ {
public function analyze(Collection $records, ServerInfo $serverinfo): Report
public function report(array $records) : string
{ {
return view("agent.meminfo", []); $report = new Report("MemInfo");
$report->setHTML(view("agent.meminfo"));
foreach ($records as $record) {
$mem = $this->parseMeminfo($record->data["memory"]);
if ($mem->usedRatio() > 0.8) {
return $report->setStatus(Status::WARNING);
}
}
return $report->setStatus(Status::ok());
} }
public function usedMemoryPoints(array $records)
public function usedMemoryPoints(Collection $records)
{ {
$used = []; $used = [];
foreach ($records as $record) { foreach ($records as $record) {
...@@ -31,7 +46,7 @@ class MemInfo extends Sensor ...@@ -31,7 +46,7 @@ class MemInfo extends Sensor
return $used; return $used;
} }
public function cachedMemoryPoints(array $records) public function cachedMemoryPoints(Collection $records)
{ {
$points = []; $points = [];
foreach ($records as $record) { foreach ($records as $record) {
...@@ -45,18 +60,6 @@ class MemInfo extends Sensor ...@@ -45,18 +60,6 @@ class MemInfo extends Sensor
return $points; return $points;
} }
public function status(array $records) : int
{
foreach ($records as $record) {
$mem = $this->parseMeminfo($record->data["memory"]);
if ($mem->usedRatio() > 0.8) {
return \App\Status::WARNING;
}
}
return \App\Status::OK;
}
// used = total - free - cached // used = total - free - cached
const MEMTOTAL = "/^MemTotal:\\s+([0-9]+) kB$/m"; const MEMTOTAL = "/^MemTotal:\\s+([0-9]+) kB$/m";
const MEMFREE = "/^MemFree:\\s+([0-9]+) kB$/m"; const MEMFREE = "/^MemFree:\\s+([0-9]+) kB$/m";
......
...@@ -2,22 +2,33 @@ ...@@ -2,22 +2,33 @@
namespace App\Sensor; namespace App\Sensor;
use \App\Sensor; use App\Sensor;
use App\Status;
use App\ServerInfo;
use App\Report;
use Illuminate\Database\Eloquent\Collection;
/** /**
* Parse netstat * Parse netstat
* *
* @author tibo * @author tibo
*/ */
class Netstat extends Sensor class Netstat implements Sensor
{ {
public function report(array $records) : string public function analyze(Collection $records, ServerInfo $serverinfo): Report
{ {
return view("agent.netstat", []); $report = new Report("Netstat : retransmitted TCP segments");
$report->setHTML(view("agent.netstat"))
->setStatus(Status::ok());
return $report;
} }
public function points(array $records) : array
public function points(Collection $records) : array
{ {
if (count($records) == 0) { if (count($records) == 0) {
return []; return [];
...@@ -48,11 +59,6 @@ class Netstat extends Sensor ...@@ -48,11 +59,6 @@ class Netstat extends Sensor
return [$dataset]; return [$dataset];
} }
public function status(array $records) : int
{
return \App\Status::OK;
}
const TCP_SENT = '/^ (\d+) segments sent out/m'; const TCP_SENT = '/^ (\d+) segments sent out/m';
const TCP_RETRANSMITTED = '/^ (\d+) segments retransmitted$/m'; const TCP_RETRANSMITTED = '/^ (\d+) segments retransmitted$/m';
......
...@@ -2,12 +2,15 @@ ...@@ -2,12 +2,15 @@
namespace App\Sensor; namespace App\Sensor;
use App\Status;
use App\HasStatus;
/** /**
* Description of Partition * Description of Partition
* *
* @author tibo * @author tibo
*/ */
class Partition class Partition implements HasStatus
{ {
public $filesystem = ""; public $filesystem = "";
public $blocks = 0; public $blocks = 0;
...@@ -34,4 +37,15 @@ class Partition ...@@ -34,4 +37,15 @@ class Partition
{ {
return (int) round($this->blocks / 1E6); return (int) round($this->blocks / 1E6);
} }
public function status() : Status
{
if ($this->usedPercent() > 80) {
return Status::warning();
} elseif ($this->usedPercent() > 95) {
return Status::error();
}
return Status::ok();
}
} }
<?php
namespace App\Sensor;
use Carbon\Carbon;
/**
* Represents the time evolution of a single partition.
*/
class PartitionDelta
{
private $start;
private $end;
public function __construct(Partition $start, Partition $end)
{
if ($start->filesystem !== $end->filesystem) {
throw new \Exception("Comparing different filesystems!");
}
$this->start = $start;
$this->end = $end;
}
public function filesystem() : string
{
return $this->start->filesystem;
}
/**
* Return difference of the number of used blocks.
* @return int
*/
private function deltaBlocks() : int
{
return $this->end->used - $this->start->used;
}
/**
* Return time difference between 2 partitions
* @return int
*/
private function deltaT() : int
{
return $this->end->time - $this->start->time;
}
/**
* Time in second, before this partition gets full.
* @return int
*/
public function timeUntillFull() : int
{
if ($this->deltaBlocks() <= 0) {
return PHP_INT_MAX;
}
return ($this->end->blocks - $this->end->used) / $this->deltaBlocks()
* $this->deltaT();
}
public function timeUntilFullForHumans() : string
{
return Carbon::createFromTimeStamp($this->timeUntillFull())->diffForHumans();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment