Complete rewrite of the monitoring daemon in PHP
This commit is contained in:
parent
6e5f4e9736
commit
797dade52b
2 changed files with 249 additions and 2 deletions
238
app/Console/Commands/RunMonitoring.php
Normal file
238
app/Console/Commands/RunMonitoring.php
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use \Exception;
|
||||||
|
use Illuminate\Queue\Console\MonitorCommand;
|
||||||
|
|
||||||
|
class RunMonitoring extends Command
|
||||||
|
{
|
||||||
|
private $rounds = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'monitolite:monitoring:run {rounds?}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Executes all the monitoring tasks';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Storing all the results for output
|
||||||
|
*/
|
||||||
|
private $results;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$count = 0;
|
||||||
|
$rounds = $this->argument('rounds') ?? $this->rounds;
|
||||||
|
|
||||||
|
// Getting pending tasks
|
||||||
|
$tasks = app('db')->select('
|
||||||
|
SELECT id, host, type, params
|
||||||
|
FROM tasks
|
||||||
|
WHERE ( DATE_SUB(now(), INTERVAL frequency SECOND) > last_execution OR last_execution IS NULL )
|
||||||
|
AND active = 1
|
||||||
|
ORDER BY last_execution ASC
|
||||||
|
LIMIT :limit
|
||||||
|
', [
|
||||||
|
'limit' => $rounds
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (is_null($tasks) || count($tasks) == 0) {
|
||||||
|
$this->info('No task to process, going back to sleep');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->info('I have '.count($tasks).' to process. Better get started ...');
|
||||||
|
|
||||||
|
$this->newLine();
|
||||||
|
$bar = $this->output->createProgressBar(count($tasks));
|
||||||
|
$bar->start();
|
||||||
|
|
||||||
|
foreach ($tasks as $task) {
|
||||||
|
$last_status = $new_status = $output = null;
|
||||||
|
$bar->advance();
|
||||||
|
|
||||||
|
// Getting current task last status
|
||||||
|
$query = DB::table('tasks_history')
|
||||||
|
->select('status')
|
||||||
|
->where('task_id', $task->id)
|
||||||
|
->orderBy('datetime', 'DESC')
|
||||||
|
->first()
|
||||||
|
;
|
||||||
|
if ($query !== false && ! is_null($query)) {
|
||||||
|
$last_status = $query->status;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch ($task->type) {
|
||||||
|
case 'ping':
|
||||||
|
$new_status = $this->checkPing($task);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http':
|
||||||
|
$new_status = $this->checkHttp($task);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Nothing to do here
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveHistory($task, true);
|
||||||
|
}
|
||||||
|
catch(MonitoringException $e) {
|
||||||
|
$this->saveHistory($task, false, $e->getMessage());
|
||||||
|
}
|
||||||
|
catch(Exception $e) {
|
||||||
|
$this->saveHistory($task, false, $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$bar->finish();
|
||||||
|
$this->newLine(2);
|
||||||
|
|
||||||
|
if (!empty($this->results)) {
|
||||||
|
$this->table(
|
||||||
|
['Host', 'Result', 'Message'],
|
||||||
|
$this->results
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function saveHistory($task, $status, $output = null) {
|
||||||
|
$date = date('Y-m-d H:i:s');
|
||||||
|
|
||||||
|
$this->results[] = [
|
||||||
|
'host' => $task->host,
|
||||||
|
'result' => $status === true ? 'OK' : 'FAILED',
|
||||||
|
'message' => $output
|
||||||
|
];
|
||||||
|
|
||||||
|
$insert = DB::table('tasks_history')
|
||||||
|
->insert([
|
||||||
|
'status' => $status === true ? 1 : 0,
|
||||||
|
'datetime' => $date,
|
||||||
|
'output' => $output ?? '',
|
||||||
|
'task_id' => $task->id
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (false !== $insert) {
|
||||||
|
DB::table('tasks')
|
||||||
|
->where('id', $task->id)
|
||||||
|
->update([
|
||||||
|
'last_execution' => $date
|
||||||
|
])
|
||||||
|
;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function checkPing($task) {
|
||||||
|
if (! function_exists('exec') || ! is_callable('exec')) {
|
||||||
|
throw new MonitoringException('The "exec" command is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Different command line for different OS
|
||||||
|
switch (strtolower(php_uname('s'))) {
|
||||||
|
case 'darmin':
|
||||||
|
$cmd = 'ping -n 1 -t 5';
|
||||||
|
break;
|
||||||
|
case 'windows':
|
||||||
|
$cmd = 'ping /n 1 /w 5';
|
||||||
|
break;
|
||||||
|
case 'linux':
|
||||||
|
case 'freebsd':
|
||||||
|
default:
|
||||||
|
$cmd = 'ping -c 1 -W 5';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If command failed
|
||||||
|
if (false === $exec = exec($cmd.' '.$task->host, $output, $code)) {
|
||||||
|
throw new MonitoringException('Unable to execute ping command');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If command returned a non-zero code
|
||||||
|
if ($code > 0) {
|
||||||
|
throw new MonitoringException('Ping task failed ('.$exec.')');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double check
|
||||||
|
$output = implode(' ', $output);
|
||||||
|
// Looking for the 100% package loss output
|
||||||
|
if (preg_match('~([0-9]{1,3})\.[0-9]{0,2}% +(packet)? +loss~', $output, $matches)) {
|
||||||
|
if (! empty($matches[1])) {
|
||||||
|
if (floatval($matches[1]) == 100) {
|
||||||
|
throw new MonitoringException('Packet loss detected ('.($matches[0] ?? 'n/a').')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else everything is fine
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function checkHttp($task) {
|
||||||
|
// Preparing cURL
|
||||||
|
$opts = [
|
||||||
|
CURLOPT_HTTPGET => true,
|
||||||
|
CURLOPT_FRESH_CONNECT => true,
|
||||||
|
CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
|
||||||
|
CURLOPT_SSL_VERIFYHOST => 2,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_MAXREDIRS => 3,
|
||||||
|
CURLOPT_FAILONERROR => true,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 10,
|
||||||
|
CURLOPT_URL => trim($task->host)
|
||||||
|
];
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, $opts);
|
||||||
|
if ($result = curl_exec($ch)) {
|
||||||
|
|
||||||
|
// We have nothing to check into the page
|
||||||
|
// So for me, this is a big YES
|
||||||
|
if (empty($task->params)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// We are looking for a string in the page
|
||||||
|
else {
|
||||||
|
if (strpos($result, $task->params) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new MonitoringException('Cannot find the required string into the page');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new MonitoringException(curl_error($ch), curl_errno($ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MonitoringException extends Exception {}
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace App\Console;
|
||||||
use Illuminate\Console\Scheduling\Schedule;
|
use Illuminate\Console\Scheduling\Schedule;
|
||||||
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
|
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
|
||||||
use App\Console\Commands\SyncCustomers;
|
use App\Console\Commands\SyncCustomers;
|
||||||
|
use App\Console\Commands\CleanHistory;
|
||||||
|
use App\Console\Commands\RunMonitoring;
|
||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
class Kernel extends ConsoleKernel
|
||||||
{
|
{
|
||||||
|
@ -14,7 +16,9 @@ class Kernel extends ConsoleKernel
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $commands = [
|
protected $commands = [
|
||||||
SyncCustomers::class
|
SyncCustomers::class,
|
||||||
|
CleanHistory::class,
|
||||||
|
RunMonitoring::class
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +34,12 @@ class Kernel extends ConsoleKernel
|
||||||
* You may safely remove this scheduled task
|
* You may safely remove this scheduled task
|
||||||
*/
|
*/
|
||||||
if (env('CMS_ENABLE_SYNC') == true) {
|
if (env('CMS_ENABLE_SYNC') == true) {
|
||||||
$schedule->command('customers:sync')->hourly();
|
$schedule->command('monitolite:customers:sync')->hourly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main monitoring task
|
||||||
|
*/
|
||||||
|
$schedule->command('monitolite:monitoring:run')->everyMinute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue