Простой монитор времени безотказной работы PHP для серверов Windows и коммутаторов Cisco


Я написал (хорошо скомпонованный из кода других людей) очень простой монитор безотказной работы для наших серверов - это просто монитор ICMP (ping), и он очень хорошо работает для нашего ограниченного количества серверов (20 или около того) и очень быстро. Вот код (фактические функции проверки пинга, я думаю, основаны на работе Бирка Дженсена (http://birk-jensen.dk/2010/09/php-ping /), и я только что использовал его функции для отображения зеленого круга PNG, когда все готово, и красных для каждого сервер, который не работает (если таковой имеется).

<html>
<head>
<style type='text/css'>

*{
    font-family:verdana,tahoma,arial;
    font-size:17px;
}

.light{width:30px;}

h1{
        font-size:25px;
}
</style>



<meta http-equiv="refresh" content="30">
</head>
<body>
<?php

$time1=date('H:i:s');
echo "Last Refresh Time = $time1<br/><hr/>";

error_reporting(0);

/*-----------------------------------------------------------------------------------------*/    
    // Checksum calculation function
    function icmpChecksum($data)
    {
    if (strlen($data)%2)
    $data .= "\x00";

    $bit = unpack('n*', $data);
    $sum = array_sum($bit);

    while ($sum >> 16)
    $sum = ($sum >> 16) + ($sum & 0xffff);

    return pack('n*', ~$sum);
    }


/*-----------------------------------------------------------------------------------------*/
    function PingTry1($pingaddress){
    // Making the package
    $type= "\x08";
    $code= "\x00";
    $checksum= "\x00\x00";
    $identifier = "\x00\x00";
    $seqNumber = "\x00\x00";
    $data= "testing123";
    $package = $type.$code.$checksum.$identifier.$seqNumber.$data;
    $checksum = icmpChecksum($package); // Calculate the checksum
    $package = $type.$code.$checksum.$identifier.$seqNumber.$data;
    // And off to the sockets
    $socket = socket_create(AF_INET, SOCK_RAW, 1);

    socket_set_option ( $socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>1, "usec"=>0) );
    socket_connect($socket, $pingaddress, null);

    $startTime = microtime(true);
    socket_send($socket, $package, strLen($package), 0);
    if (socket_read($socket, 255)) {
    return true;
    }
    else{
    return false;
    }

    socket_close($socket);

    }
/*-----------------------------------------------------------------------------------------*/
   function DoTheCheck($name,$ip){
       global $errors;
       global $j;
       if (PingTry1($ip)==1){
    //do nothing
                      }else{
                      $j++;
                      $errors[$j] = "$name --> $ip";

                      }

    }
/*-----------------------------------------------------------------------------------------*/

//READ IN THE INI FILE INTO $filedata Array

$myFile1="hosts.ini";
$filehandle1 = fopen($myFile1, 'r') or die("Couldn't open file [$myFile1]");
$number1=count(file($myFile1));;
$filedata = fread($filehandle1, filesize($myFile1));
fclose($filehandle1);

// Create an array with each line of the file
$array1 = explode("\r\n", $filedata);

unset($filedata); //free up a bit of memory

foreach ($array1 as &$line) { // step through the array, line by line
    if (!empty($line)){
list ($name,$ip)=split(",",$line);
    DoTheCheck($name,$ip);
                 }
}


    if ($errors){

            echo 'The Following Hosts are down - <br/><br/><table>';

foreach ($errors as &$value) {
    $k++;
    echo '<tr><td><img class="light" src="red.png" /></td><td>'.$errors[$k].'</td></tr>';
}
echo '</tr></table>';
    }
else{echo '<img class="light" src="green.png" /><h1>ALL IPS ARE UP!</h1>';}




    ?>
    </body>
    </html>

Приведенный выше код отлично работает для серверов, но, похоже, он вообще не работает для коммутаторов Cisco - возможно, это как-то связано с тем, как он выполняет свой "пинг" как таковой.

Я давно не работал над этим сценарием из-за обязательств перед колледжем и т. Д., Но я вернулся к тому, чтобы проводить как можно больше исследований в Google, но, по общему признанию, я в лучшем случае PHP n00b 2 или 3 уровня. Сегодня я нашел пару решений, которые работают для коммутаторов, но у них есть 5 или 6 вторые периоды ожидания, что неприемлемо, так как я хочу, чтобы система выполняла цикл как можно больше и как можно более чисто, и регистрировала время простоя для последующего построения графиков.

Например - я пробовал это:

   function ping($host, $timeout = 1) {
                /* ICMP ping packet with a pre-calculated checksum */
                $package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
                $socket  = socket_create(AF_INET, SOCK_RAW, 1);
                socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
                socket_connect($socket, $host, null);

                $ts = microtime(true);
                socket_send($socket, $package, strLen($package), 0);
                if (socket_read($socket, 255))
                        $result = microtime(true) - $ts;
                else    $result = false;
                socket_close($socket);

                return $result;
        }

А также это:

    $url         = '192.168.1.1'; 
    $socket       = ( bool )false; 
    $error      = ( bool )false; 

    $socket = @fsockopen( $url, 23, $errno, $errstr, 1 ) or $error = ( bool )true; 

    if ( ( $socket ) && ( !$error ) ) 
    { 
        echo "bound";
        /* socket is bound - do something */ 
    } 
    else 
    { 
        echo "not bound , [$errstr]";
        /* socket is dead - errors are in $errno & $errstr */ 
    }  

if ($socket)fclose($socket);

И оба они, похоже, работают, когда хост подключен к Сети, но если я дам ему IP-адрес, которого не существует (для тестирования, как если бы хост был отключен), для тайм-аута на одном IP-адресе требуется около 5 или более секунд, что слишком медленно для моего потребности.

Возможно ли это сделать с помощью pcntl_fork или даже curl с многопоточностью? или несколько вызовов "exec" или даже AJAX (я готов попробовать что угодно на этом этапе)

Или какой-то уровень данных (уровень 2) код сканирования Mac также был бы отличным - я не ожидаю, что кто-нибудь напишет полный код, но я уверен, что кто-то, кто делал подобные вещи раньше, имел бы хорошее представление о подводных камнях и о том, как их обойти.

Итак, вкратце - простое и легкое исправление было бы неплохо (я буду продолжать мечтать:-D), но любая помощь или совет вообще очень ценятся.

РЕДАКТИРОВАТЬ - после некоторых советов попробовать Net_Ping в PEAR у меня есть следующий код:

<?php

$time1=date('H:i:s');
echo "Last Refresh Time = $time1<br/><hr/>";

//not sure if still needed -       error_reporting(0);


require_once "Net/Ping.php";
$ping = Net_Ping::factory();

$ping->setArgs(array('count' => 2, 'ttl' => 50, 'timeout' => 1));
/*---------------------------------------------------------------------*/
function DoPing($ip)
{
    global $ping;

    $results = $ping->ping($ip);
    if ($results->_loss==0) {return true;}else{return false;}
}
/*---------------------------------------------------------------------------------*/
   function DoTheCheck($name,$ip){
       global $errors;
       global $j;

       if (DoPing($ip)==1){
    //do nothing
                      }else{
                      $j++;
                      $errors[$j] = "$name --> $ip";
                      }
    }
/*-----------------------------------------------------------------------------------*/

//READ IN THE INI FILE INTO $filedata Array

$myFile1="hosts.ini";
$filehandle1 = fopen($myFile1, 'r') or die("Couldn't open file [$myFile1]");
$number1=count(file($myFile1));;
$filedata = fread($filehandle1, filesize($myFile1));
fclose($filehandle1);

// Create an array with each line of the file
$array1 = explode("\r\n", $filedata);

unset($filedata); //free up a bit of memory

foreach ($array1 as &$line) { // step through the array, line by line
    if (  (!empty($line)) && (!strstr($line,'##'))  ) {
list ($name,$ip)=split(",",$line);
    DoTheCheck($name,$ip);
                 }
}

    if ($errors){

            echo 'The Following Hosts are down - <br/><br/><table>';

foreach ($errors as &$value) {
    $k++;
    echo '<tr><td><img class="light" src="red.png" /></td><td>'.$errors[$k].'</td></tr>';
}
echo '</tr></table>';
    }
else{echo '<img class="light" src="green.png" /><h1>ALL IPS ARE UP!</h1>';}

?>

Но это слишком медленно... требуется около минуты или двух, чтобы проверить около 20 серверов и 10 коммутаторов. Мне нужно добавить около 100 переключателей, так что это будет только медленнее. Должен быть лучший способ сделать это. Опять же, любая помощь всегда очень ценится. Я, вероятно, попробую Мунин, но реально я мне нужно что-то, что я могу интегрировать во внутреннюю сеть моей компании (PHP).

Author: TheDavil, 2011-05-27

1 answers

Вы уже пробовали надлежащую систему мониторинга, такую как Мунин? известно, что он работает, в отличие от вашего сценария ручной работы. Я использую его для мониторинга моего подключения к Интернету и наличия доступных серверов; для этого он предоставляет плагин ping. Мунин также отправляет письма в случае ошибок и рисует красивые графики.

Есть также Нагио и Кактусы, но я обнаружил, что Мунин наиболее прост в настройке.

Если вы действительно действительно хотите продолжать использовать PHP в одиночку, взгляните на PEAR Пакет net_ping, который предоставляет API для отправки пингов.

Изменить: Скорость

Поскольку пинг всех хостов один за другим выполняется слишком медленно, вам необходимо распараллелить свои пинги. Net_Ping не поддерживает это, поэтому вам придется запускать несколько процессов PHP параллельно. Используйте либо функции pcntl PHP, либо одну из функций shell_exec. Сценарию ping просто будет предоставлен хост для пинга в качестве единственного параметра командной строки, и он регистрирует результат пинга в общем файле журнала. Главный скрипт ожидает завершения всех сценариев ping и собирает зарегистрированную информацию.

 1
Author: cweiske, 2011-06-02 17:42:31