适用于 Windows 服务器和 Cisco 交换机的简单 PHP 正常运行时间监视器

发布于 2024-11-10 06:04:31 字数 7636 浏览 3 评论 0原文

我为我们的服务器编写了(从其他人的代码中拼凑而成)一个非常简单的正常运行时间监视器 - 它只是一个 ICMP (ping) 监视器,它对于我们有限数量的服务器(20 台左右)非常有效,而且速度非常快。 这是代码(我认为实际的 ping 测试函数基于 Birk Jensen 的工作( 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>

效果很好,但对于思科交换机来说似乎根本不起作用——可能与它的“ping”方式有关,

我还没有这样做 。由于大学承诺等原因,我花了很长时间来编写这个脚本,但我已经尽可能多地在谷歌上进行研究,但不可否认,我最多只是 2 或 3 级 PHP n00b。 今天我发现了一些适用于交换机的解决方案,但它们有 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),但非常感谢任何帮助或建议。

编辑 - 在尝试 PEAR 中的 Net_Ping 的一些建议之后,我得到了以下代码:

<?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 个开关,因此速度只会变得更慢。必须有更好的方法来做到这一点。再次强调,我们非常感谢任何帮助。我可能会尝试 Munin,但实际上我需要一些可以集成到我公司的 Intranet (PHP) 中的东西。

I've written (well cobbled together from other people's code) a very simple uptime monitor for our servers - it's just an ICMP (ping) monitor and it works very well for our limited amount of servers (20 or so), and very fast.
Here's the code (the actual ping test functions I think are based on Birk Jensen's work ( http://birk-jensen.dk/2010/09/php-ping/ ), and I've just utilised his functions to display a green circle PNG when everything is up and red ones for each server that's down (if any).

<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>

The code above works great for servers but it doesn't seem to work at all for Cisco switches - probably something to do with the way it does its 'ping' as such.

I haven't done any work on this script in ages because of college commitments etc but I've gone back to doing as much google research as I can, but admittedly I'm a level 2 or 3 PHP n00b at best.
Today I found a couple of solutions that work for switches, but they have 5 or 6 second timeout periods, which is unacceptable as I want the system to loop as much as possible and as cleanly as possible, and log downtime for graphing later on.

For Example - I've tried this:

   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;
        }

and also this:

    $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);

And they both seem to work when the host is online, but if I give it an IP that doesn't exist (for testing, as if host was offline), it takes about 5 or more seconds to time out on a single IP, which is just too slow for my needs.

Would it be possible to do this using pcntl_fork or even curl with multi-threading ? or multiple 'exec' calls or AJAX even (I'm willing to try anything at this stage)

or some sort of Data Layer (layer 2) Mac scanning code would be great either - I don't expect anyone to write the full code, but I'm sure somebody that has done this sort of thing before would have a good idea of the pitfalls and how to get around them.

So in summary - a simple and easy fix would be nice ( I'll Keep Dreaming :-D ) but any help or advice at all is much appreciated.

EDIT - after some advice to try Net_Ping in PEAR I've got the following code:

<?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>';}

?>

but that is too slow... takes about a minute or two to check about 20 servers and 10 switches. I need to add about 100 switches so it's only going to get slower. There must be a better way to do this. Again, any help is always very much appreciated. I will probably try Munin but realistically I need something I can integrate into my Company's Intranet (PHP).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

请恋爱 2024-11-17 06:04:31

您是否尝试过像 Munin 这样的合适的监控系统?众所周知,它是有效的,而不是你手工制作的脚本。我用它来监控我的互联网连接以及服务器是否可用;它为此提供了一个 ping 插件。 Munin 还会在出现错误时发送邮件并绘制漂亮的图表。

还有 Nagios 和 Cacti,但我发现 Munin 最容易设置。

如果您真的真的想继续单独使用 PHP,请查看 PEAR 的 Net_Ping 包,它提供了发送 ping 的 API。

编辑:速度

由于逐个 ping 所有主机的速度太慢,因此您需要并行化 ping。 Net_Ping 不支持这一点,因此您必须并行运行多个 PHP 进程。使用 PHP 的 pcntl 函数或 shell_exec 函数之一。
ping 脚本只需将要 ping 的主机作为唯一的命令行参数,并将 ping 的结果记录在共享日志文件中。主脚本等待所有 ping 脚本结束并收集记录的信息。

Did you try a proper monitoring system like Munin yet? it is known to work, as opposed to your handcrafted script. I use it to monitor my internet connection and if servers are available; it provides a ping plugin for that. Munin does also send mails in case of errors and draws nice graphs.

There are also Nagios and Cacti, but I've found Munin the most easy to setup.

If you really really really want to keep using PHP alone, have a look at PEAR's Net_Ping package which provides an API to send pings.

Edit: Speed

Since pinging all the hosts one after another is too slow, you need to parallelize your pings. Net_Ping doesn't support this, so you have to run several PHP processes in parallel. Use either PHP's pcntl functions or one of the shell_exec functions.
The ping script would simply be given the host to ping as only command line parameter, and it logs the result of the ping in a shared log file. The main script waits until all ping scripts ended and collects the logged information.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文