312 lines
10 KiB
PHP
312 lines
10 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* Enhanced Router Monitoring Service
|
||
|
|
* Runs every 2 minutes to collect comprehensive router data
|
||
|
|
* Usage: */2 * * * * cd /var/www/html/newdesign1/system && php -f router_monitor.php
|
||
|
|
*/
|
||
|
|
|
||
|
|
include "../init.php";
|
||
|
|
|
||
|
|
$isCli = php_sapi_name() === 'cli';
|
||
|
|
if (!$isCli) {
|
||
|
|
echo "<pre>";
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "Router Monitor Started: " . date('Y-m-d H:i:s') . "\n";
|
||
|
|
|
||
|
|
use PEAR2\Net\RouterOS;
|
||
|
|
use RouterOS\Exceptions\Socket\TimeoutException;
|
||
|
|
use RouterOS\Exceptions\Socket\ConnectionException;
|
||
|
|
|
||
|
|
class RouterMonitor {
|
||
|
|
private $config;
|
||
|
|
private $db;
|
||
|
|
|
||
|
|
public function __construct() {
|
||
|
|
global $config;
|
||
|
|
$this->config = $config;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Monitor all enabled routers
|
||
|
|
*/
|
||
|
|
public function monitorAllRouters() {
|
||
|
|
$routers = ORM::for_table('tbl_routers')->where('enabled', '1')->find_many();
|
||
|
|
|
||
|
|
echo "Monitoring " . count($routers) . " routers...\n";
|
||
|
|
|
||
|
|
foreach ($routers as $router) {
|
||
|
|
$this->monitorRouter($router);
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "Router monitoring completed: " . date('Y-m-d H:i:s') . "\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Monitor individual router
|
||
|
|
*/
|
||
|
|
private function monitorRouter($router) {
|
||
|
|
$routerId = $router['id'];
|
||
|
|
$routerName = $router['name'];
|
||
|
|
$ipAddress = $router['ip_address'];
|
||
|
|
|
||
|
|
echo "Checking router: $routerName ($ipAddress)...\n";
|
||
|
|
|
||
|
|
// Check if router is in circuit breaker state
|
||
|
|
if ($this->isCircuitBreakerActive($routerId)) {
|
||
|
|
echo " - Circuit breaker active, skipping\n";
|
||
|
|
$this->updateRouterStatus($router, 'Offline', 'Circuit breaker active');
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Perform comprehensive check
|
||
|
|
$monitoringData = $this->performRouterCheck($router);
|
||
|
|
|
||
|
|
// Update database with results
|
||
|
|
$this->updateRouterData($router, $monitoringData);
|
||
|
|
|
||
|
|
// Check for alerts
|
||
|
|
$this->checkAlerts($router, $monitoringData);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Perform comprehensive router check
|
||
|
|
*/
|
||
|
|
private function performRouterCheck($router) {
|
||
|
|
$data = [
|
||
|
|
'timestamp' => date('Y-m-d H:i:s'),
|
||
|
|
'ping_status' => false,
|
||
|
|
'api_status' => false,
|
||
|
|
'uptime' => null,
|
||
|
|
'free_memory' => null,
|
||
|
|
'total_memory' => null,
|
||
|
|
'cpu_load' => null,
|
||
|
|
'temperature' => null,
|
||
|
|
'voltage' => null,
|
||
|
|
'error' => null
|
||
|
|
];
|
||
|
|
|
||
|
|
// Step 1: Ping check
|
||
|
|
$data['ping_status'] = $this->pingRouter($router['ip_address']);
|
||
|
|
|
||
|
|
if (!$data['ping_status']) {
|
||
|
|
$data['error'] = 'Ping failed';
|
||
|
|
return $data;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 2: RouterOS API check
|
||
|
|
try {
|
||
|
|
$client = Mikrotik::getClient($router['ip_address'], $router['username'], $router['password'], 8);
|
||
|
|
|
||
|
|
if (!$client) {
|
||
|
|
$data['error'] = 'Failed to create RouterOS client';
|
||
|
|
return $data;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get system resource data
|
||
|
|
$resourceRequest = new RouterOS\Request('/system resource print');
|
||
|
|
$resourceResponse = $client->sendSync($resourceRequest, 5);
|
||
|
|
$resourceData = $resourceResponse->getAllOfType(RouterOS\Response::TYPE_DATA)[0];
|
||
|
|
|
||
|
|
// Get system health data
|
||
|
|
$healthRequest = new RouterOS\Request('/system health print');
|
||
|
|
$healthResponse = $client->sendSync($healthRequest, 5);
|
||
|
|
$healthData = $healthResponse->getAllOfType(RouterOS\Response::TYPE_DATA)[0];
|
||
|
|
|
||
|
|
// Extract data
|
||
|
|
$data['api_status'] = true;
|
||
|
|
$data['uptime'] = $resourceData->getProperty('uptime');
|
||
|
|
$data['free_memory'] = (int)$resourceData->getProperty('free-memory');
|
||
|
|
$data['total_memory'] = (int)$resourceData->getProperty('total-memory');
|
||
|
|
$data['cpu_load'] = (int)$resourceData->getProperty('cpu-load');
|
||
|
|
$data['temperature'] = $healthData->getProperty('temperature');
|
||
|
|
$data['voltage'] = $healthData->getProperty('voltage');
|
||
|
|
|
||
|
|
// Clear circuit breaker on successful connection
|
||
|
|
$this->clearCircuitBreaker($router['id']);
|
||
|
|
|
||
|
|
} catch (TimeoutException $e) {
|
||
|
|
$data['error'] = 'API timeout';
|
||
|
|
$this->incrementCircuitBreaker($router['id']);
|
||
|
|
} catch (ConnectionException $e) {
|
||
|
|
$data['error'] = 'API connection failed';
|
||
|
|
$this->incrementCircuitBreaker($router['id']);
|
||
|
|
} catch (Exception $e) {
|
||
|
|
$data['error'] = 'API error: ' . $e->getMessage();
|
||
|
|
$this->incrementCircuitBreaker($router['id']);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $data;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Ping router
|
||
|
|
*/
|
||
|
|
private function pingRouter($ipAddress, $timeout = 5, $count = 3) {
|
||
|
|
$ipParts = explode(':', $ipAddress);
|
||
|
|
$ip = $ipParts[0];
|
||
|
|
$escapedIp = escapeshellarg($ip);
|
||
|
|
|
||
|
|
exec("ping -c $count -W $timeout $escapedIp 2>&1", $output, $returnVar);
|
||
|
|
|
||
|
|
return $returnVar === 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update router status in database
|
||
|
|
*/
|
||
|
|
private function updateRouterStatus($router, $status, $error = null) {
|
||
|
|
$router->status = $status;
|
||
|
|
$router->last_check = date('Y-m-d H:i:s');
|
||
|
|
|
||
|
|
if ($error) {
|
||
|
|
$router->last_error = $error;
|
||
|
|
}
|
||
|
|
|
||
|
|
$router->save();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Update router data in database
|
||
|
|
*/
|
||
|
|
private function updateRouterData($router, $data) {
|
||
|
|
// Update main router table
|
||
|
|
$router->status = ($data['api_status'] && $data['ping_status']) ? 'Online' : 'Offline';
|
||
|
|
$router->last_check = $data['timestamp'];
|
||
|
|
$router->last_error = $data['error'];
|
||
|
|
$router->save();
|
||
|
|
|
||
|
|
// Store detailed monitoring data
|
||
|
|
$this->storeMonitoringData($router['id'], $data);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Store detailed monitoring data
|
||
|
|
*/
|
||
|
|
private function storeMonitoringData($routerId, $data) {
|
||
|
|
// Create monitoring record
|
||
|
|
$monitoring = ORM::for_table('tbl_router_monitoring')->create();
|
||
|
|
$monitoring->router_id = $routerId;
|
||
|
|
$monitoring->timestamp = $data['timestamp'];
|
||
|
|
$monitoring->ping_status = $data['ping_status'] ? 1 : 0;
|
||
|
|
$monitoring->api_status = $data['api_status'] ? 1 : 0;
|
||
|
|
$monitoring->uptime = $data['uptime'];
|
||
|
|
$monitoring->free_memory = $data['free_memory'];
|
||
|
|
$monitoring->total_memory = $data['total_memory'];
|
||
|
|
$monitoring->cpu_load = $data['cpu_load'];
|
||
|
|
$monitoring->temperature = $data['temperature'];
|
||
|
|
$monitoring->voltage = $data['voltage'];
|
||
|
|
$monitoring->error = $data['error'];
|
||
|
|
$monitoring->save();
|
||
|
|
|
||
|
|
// Clean up old records (keep last 7 days)
|
||
|
|
$cutoffDate = date('Y-m-d H:i:s', strtotime('-7 days'));
|
||
|
|
ORM::for_table('tbl_router_monitoring')
|
||
|
|
->where('timestamp', '<', $cutoffDate)
|
||
|
|
->delete_many();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check for alerts
|
||
|
|
*/
|
||
|
|
private function checkAlerts($router, $data) {
|
||
|
|
$alerts = [];
|
||
|
|
|
||
|
|
// CPU load alert
|
||
|
|
if ($data['cpu_load'] && $data['cpu_load'] > 80) {
|
||
|
|
$alerts[] = "High CPU load: {$data['cpu_load']}%";
|
||
|
|
}
|
||
|
|
|
||
|
|
// Memory usage alert
|
||
|
|
if ($data['free_memory'] && $data['total_memory']) {
|
||
|
|
$memoryUsage = (($data['total_memory'] - $data['free_memory']) / $data['total_memory']) * 100;
|
||
|
|
if ($memoryUsage > 90) {
|
||
|
|
$alerts[] = "High memory usage: " . round($memoryUsage, 1) . "%";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Temperature alert
|
||
|
|
if ($data['temperature'] && $data['temperature'] > 60) {
|
||
|
|
$alerts[] = "High temperature: {$data['temperature']}°C";
|
||
|
|
}
|
||
|
|
|
||
|
|
// Send alerts if any
|
||
|
|
if (!empty($alerts)) {
|
||
|
|
$this->sendAlerts($router, $alerts);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Send alerts
|
||
|
|
*/
|
||
|
|
private function sendAlerts($router, $alerts) {
|
||
|
|
$message = "Router {$router['name']} alerts:\n" . implode("\n", $alerts);
|
||
|
|
|
||
|
|
// Send to admin users
|
||
|
|
$users = ORM::for_table('tbl_users')->where('user_type', 'Admin')->find_many();
|
||
|
|
foreach ($users as $user) {
|
||
|
|
$this->sendSMS($user['phone'], $message);
|
||
|
|
$this->sendWhatsApp($user['phone'], $message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Circuit breaker methods
|
||
|
|
*/
|
||
|
|
private function isCircuitBreakerActive($routerId) {
|
||
|
|
if (!function_exists('apcu_fetch')) return false;
|
||
|
|
|
||
|
|
$key = "router_failures_$routerId";
|
||
|
|
$timeKey = "router_failures_{$routerId}_time";
|
||
|
|
|
||
|
|
$failureCount = apcu_fetch($key) ?: 0;
|
||
|
|
$lastFailure = apcu_fetch($timeKey) ?: 0;
|
||
|
|
|
||
|
|
return $failureCount >= 3 && (time() - $lastFailure) < 300; // 5 minutes
|
||
|
|
}
|
||
|
|
|
||
|
|
private function incrementCircuitBreaker($routerId) {
|
||
|
|
if (!function_exists('apcu_store')) return;
|
||
|
|
|
||
|
|
$key = "router_failures_$routerId";
|
||
|
|
$timeKey = "router_failures_{$routerId}_time";
|
||
|
|
|
||
|
|
$failureCount = apcu_fetch($key) ?: 0;
|
||
|
|
apcu_store($key, $failureCount + 1, 300);
|
||
|
|
apcu_store($timeKey, time(), 300);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function clearCircuitBreaker($routerId) {
|
||
|
|
if (!function_exists('apcu_delete')) return;
|
||
|
|
|
||
|
|
$key = "router_failures_$routerId";
|
||
|
|
$timeKey = "router_failures_{$routerId}_time";
|
||
|
|
|
||
|
|
apcu_delete($key);
|
||
|
|
apcu_delete($timeKey);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Send SMS
|
||
|
|
*/
|
||
|
|
private function sendSMS($phone, $message) {
|
||
|
|
// Implementation depends on your SMS provider
|
||
|
|
// This is a placeholder
|
||
|
|
echo "SMS to $phone: $message\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Send WhatsApp
|
||
|
|
*/
|
||
|
|
private function sendWhatsApp($phone, $message) {
|
||
|
|
// Implementation depends on your WhatsApp provider
|
||
|
|
// This is a placeholder
|
||
|
|
echo "WhatsApp to $phone: $message\n";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Run the monitor
|
||
|
|
$monitor = new RouterMonitor();
|
||
|
|
$monitor->monitorAllRouters();
|
||
|
|
?>
|