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 Laravel\Lumen\Console\Kernel as ConsoleKernel;
|
||||
use App\Console\Commands\SyncCustomers;
|
||||
use App\Console\Commands\CleanHistory;
|
||||
use App\Console\Commands\RunMonitoring;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
|
@ -14,7 +16,9 @@ class Kernel extends ConsoleKernel
|
|||
* @var array
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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