A simple PHP uptime monitor for Windows Servers and Cisco Switches
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).
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.
精彩评论