This script turned out to be quite useful. It's a combination of Python, crontab, MySQL and PHP. PHP is the interface to add/edit/delete servers, Python is the actual script that tries to connect to the server to see if it's up and running. MySQL is used as backend, PHP writes to it, the Python script fetches from it. Crontab schedules it to run every 5 minutes. I developed this script for my work, I needed something that would notify me if one of our servers went down. And it turned out that this solution was perfect. However, bare with me, it took me less than 4-5 hours to make this, hence the quality of the code. However, it works, which is the important part. And the code is under GPLv2 so feel free to fork it and improve the quality of the code. You can download the script here
1. #!/usr/bin/python 2. import socket 3. import MySQLdb 4. from httplib import HTTPSConnection as https 5. from urllib import urlencode 6. 7. ########################################################### 8. # 9. # Author: Caesar "sniker" Ahlenhed 10. # Contact: sniker@se.linux.org 11. # Name: PSC(Prowlserverchecker, creative, I know) 12. # Description: Reads servers listed in a database 13. # which is controlled through a webinterface 14. # and tries to connect to them, if 15. # that fails, it'll send a prowl 16. # notification that the server is down 17. # License: GPLv2 18. # Url: http://sniker.codebase.nu/ 19. # Changed: Thu Mar 18 00:28:43 CET 2011 20. # 21. # 22. # eth0 will prevail. || irc.eth0.info 23. # 24. ########################################################### 25. 26. # Start of settings 27. dbip = "127.0.0.1" # Probably don't need any changing 28. dbusername = "username" # Database username 29. dbpassword = "password" # Database password 30. database = "database" # Mysql Database 31. # End of settings 32. 33. # Dont touch anything below this unless you know what you're doing. 34. API_DOMAIN = 'prowl.weks.net' 35. __version__ = "0.3" 36. 37. def post(apikey, application, event, description, priority): 38. 39. headers = {'User-Agent': application + "/%s" % str(__version__), 40. 'Content-type': "application/x-www-form-urlencoded"} 41. 42. h = https(API_DOMAIN) 43. 44. data = { 45. 'apikey': apikey, 46. 'application': application, 47. 'event': event, 48. 'description': description, 49. 'priority': priority 50. } 51. 52. h.request( "POST", "/publicapi/add", headers = headers, body = urlencode(data)) 53. 54. def check(host, port, timeout): 55. sock = socket.socket() 56. sock.settimeout(timeout) 57. 58. try: 59. sock.connect((host, port)) 60. return True 61. except socket.timeout: 62. return False 63. except socket.error: 64. return False 65. 66. sock.close() 67. 68. db = MySQLdb.connect(host=dbip, user=dbusername, passwd=dbpassword, db=database) 69. cursor = db.cursor() 70. 71. cursor.execute("SELECT * FROM servers WHERE status = 1 ORDER BY ID asc") 72. rows = cursor.fetchall() 73. rowcount = cursor.rowcount 74. for i in range(0, rowcount): 75. if not check(rows[i][5], rows[i][6], rows[i][8]): 76. post(rows[i][1], rows[i][2], rows[i][3], rows[i][4], rows[i][7]) 77.
This is the actual script that tries to connect to the server on a specified port. Not much to say really, only that you need to change the database username, password etc. As usual. However, see the timeout option, 3 seconds is OKAY if it's inside the same data center, however, if there's two servers far away from each other, you probably want to increase the timeout to at least 5 seconds, otherwise your phone WILL get raped with false-positives YOU CAN NOW SET THE TIMEOUT FOR EACH SERVER INDIVIDUALLY FROM THE CONTROL PANEL. To make the script run every 5 minutes, we'll want to add a crontab for it, the line below will run this script every 5 min.
*/5 * * * * /path/to/psc.py >/dev/null 2>&1
You can edit your crontabs by typing
$ crontab -e
When that's done, the python script should be up and running! However, we still need a database. Which is what we'll cover now.
1. -- 2. -- Table structure for table `servers` 3. -- 4. 5. CREATE TABLE `servers` ( 6. `id` int(11) NOT NULL auto_increment, 7. `apikey` text collate utf8_unicode_ci NOT NULL, 8. `application` varchar(255) collate utf8_unicode_ci NOT NULL, 9. `event` varchar(255) collate utf8_unicode_ci NOT NULL, 10. `description` text collate utf8_unicode_ci NOT NULL, 11. `ip4` varchar(255) collate utf8_unicode_ci NOT NULL, 12. `sport` int(11) NOT NULL, 13. `priority` varchar(3) collate utf8_unicode_ci NOT NULL, 14. `timeout` int(11) NOT NULL default '5', 15. `status` int(11) NOT NULL default '1', 16. PRIMARY KEY (`id`) 17. ) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Import the SQL-file to your database, this can be done in several ways. See the README file at the bottom of this page to see an example.
This section contains the files for the webinterface. It's 4 PHP files, they are all listed below. It's nothing fancy really, edit dbClass.php with you database settings and upload it somewhere, then you should be good to go.
<?php
include "lib/dbClass.php";
$db = new dbClass();
if(isset($_GET["id"]))
$db->dbQuery("DELETE FROM servers WHERE id = ".mysql_real_escape_string($_GET["id"]));
?>
<html>
<head>
<title>IPs and server</title>
</head>
<div>
<ul>
<li><a href="addserver.php" title="Add a server">Add server</a></li>
</ul>
<p><b>Current servers are listed below(<?=$db->dbRows("servers")?>):</b></p>
<?php
$result = $db->dbQuery("SELECT * FROM servers ORDER BY id ASC");
while($row = mysql_fetch_object($result)){
?>
<ol>
<li><a href="editserver.php?id=<?=$row->id?>"><?=$row->ip4?></a> [<a href="?id=<?=$row->id?>">D</a>]</li>
<li>API key: <b><?=$row->apikey?></b></li>
<li>Description: <b><?=$row->description?></b></li>
<li>PORT: <b><?=$row->sport?></b></li>
<li>Priority: <b><?=$row->priority?></b></li>
<li>Timeout: <b><?=$row->timeout?></b></li>
<li>Status: <b><?if($row->status == 1) echo "<span style=\"color:green\">Enabled</span>"; else echo "<span style=\"color:red\">Disabled</span>";?></b></li>
</ol>
<?php
}
?>
</div>
</html>
<?php
include "lib/dbClass.php";
$db = new dbClass();
if(isset($_POST["edit"])){
if(!empty($_POST["apikey"]))
$apikey = mysql_real_escape_string($_POST["apikey"]);
else
die("<h1>You need to enter an API key!</h1>");
if(!empty($_POST["desc"]))
$desc = mysql_real_escape_string($_POST["desc"]);
else
die("<h1>You need to enter a description!</h1>");
if(!empty($_POST["ip4"]))
$ip4 = mysql_real_escape_string($_POST["ip4"]);
else
die("<h1>You need to enter an IP!</h1>");
if(!empty($_POST["port"]))
$port = mysql_real_escape_string($_POST["port"]);
else
die("<h1>You need to enter a PORT number!</h1>");
$priority = mysql_real_escape_string($_POST["priority"]);
if($_POST["timeout"] >= 0)
$timeout = mysql_real_escape_string($_POST["timeout"]);
else
$timeout = 5;
if($_POST["status"] == 1)
$status = mysql_real_escape_string($_POST["status"]);
else
$status = 0;
$db->dbQuery("UPDATE servers SET apikey = '".$apikey."', description = '".$desc."', ip4 = '".$ip4."', sport = '".$port."', priority = '".$priority."', timeout = '".$timeout."', status = '".$status."' WHERE id = ".mysql_real_escape_string($_GET["id"]));
}
if(is_numeric($_GET["id"])){
$result = $db->dbQuery("SELECT * FROM servers WHERE id = ".mysql_real_escape_string($_GET["id"]));
$row = mysql_fetch_object($result);
}else{
die("<h1>Didn't get an ID</h1>");
}
?>
<html>
<head>
<title>Edit <?=$row->ip4?></title>
</head>
<div>
<label><a href="index.php">cd ~/</a></label>
<h3>Edit <?=$row->ip4?></h3>
<form action="?id=<?=$_GET["id"]?>" method="post" name="editsrv">
<input type="text" name="apikey" value="<?=$row->apikey?>" />
<input type="text" name="desc" value="<?=$row->description?>" />
<input type="text" name="ip4" value="<?=$row->ip4?>" />
<input type="text" name="port" value="<?=$row->sport?>" />
<input type="text" name="timeout" value="<?=$row->timeout?>" />
<select name="priority">
<option value="-2">-2 (Very low)</option>
<option value="-1">-1 (Moderate)</option>
<option value="0" selected="yes">0 (Normal)</option>
<option value="1">1 (High)</option>
<option value="2">2 (Emergency, bypasses quiet hours)</option>
</select>
<input type="checkbox" name="status" value="1" <?if($row->status == 1) echo 'checked'?> /> Enabled?
<input type="submit" name="edit" value="Edit!" />
</form>
</div>
</html>
<?php
include "lib/dbClass.php";
$db = new dbClass();
if(isset($_POST["add"])){
if(!empty($_POST["apikey"]))
$apikey = mysql_real_escape_string($_POST["apikey"]);
else
die("<h1>You need to enter an API key!</h1>");
if(!empty($_POST["desc"]))
$desc = mysql_real_escape_string($_POST["desc"]);
else
die("<h1>You need to enter a description!</h1>");
if(!empty($_POST["ip4"]))
$ip4 = mysql_real_escape_string($_POST["ip4"]);
else
die("<h1>You need to enter an IP!</h1>");
if(!empty($_POST["port"]))
$port = mysql_real_escape_string($_POST["port"]);
else
die("<h1>You need to enter a PORT number!</h1>");
$priority = mysql_real_escape_string($_POST["priority"]);
if($_POST["timeout"] >= 0)
$timeout = mysql_real_escape_string($_POST["timeout"]);
else
$timeout = 5;
if($_POST["status"] == 1)
$status = mysql_real_escape_string($_POST["status"]);
else
$status = 0;
$db->dbQuery("INSERT INTO servers(apikey, application, event, description, ip4, sport, priority, timeout, status) VALUES('".$apikey."','PSC', 'DOWN', '".$desc."', '".$ip4."', '".$port."', '".$priority."', '".$timeout."', '".$status."')");
header("Location: index.php");
}
?>
<html>
<head>
<title>Add new server</title>
</head>
<div>
<label><a href="index.php">cd ~/</a></label>
<h3>Add new server!</h3>
<form action="" method="post" name="addsrv">
<input type="text" name="apikey" value="API Key" />
<input type="text" name="desc" value="Description" />
<input type="text" name="ip4" value="IPv4 to check" />
<input type="text" name="port" value="Port to check" />
<input type="text" name="timeout" value="Timeout(in seconds, 5 is default)" />
<select name="priority">
<option value="-2">-2 (Very low)</option>
<option value="-1">-1 (Moderate)</option>
<option value="0" selected="yes">0 (Normal)</option>
<option value="1">1 (High)</option>
<option value="2">2 (Emergency, bypasses quiet hours)</option>
</select>
<input type="checkbox" name="status" value="1" checked />Enabled?
<input type="submit" name="add" value="Add server!" />
</form>
</div>
</html>
<?php
class dbClass{
var $conn;
function __construct(){
$this->conn = mysql_connect("localhost", "username", "password") or die(mysql_error());
mysql_select_db("database");
}
function dbQuery($query){
$result = mysql_query($query) or die("<b>".mysql_error()."</b><br />".htmlentities($query));
return $result;
}
function dbResult($query){
return mysql_fetch_object($query);
}
function dbRows($table){
$query = $this->dbQuery("SELECT COUNT(id) FROM ".$table);
$result = mysql_fetch_array($query) or die(mysql_error());
return $result[0];
}
function dbLastId($table){
$query = $this->dbQuery(" SELECT LAST_INSERT_ID(id) FROM ".$table." ORDER by id DESC LIMIT 1");
$result = mysql_fetch_array($query) or die(mysql_error());
return $result[0];
}
function __destruct(){
mysql_close($this->conn) or die(mysql_error());
}
}
?>
Remember to change username, password etc to match the username and password of your database!
Since this is version 0.3 I don't have any built in function for handling users and/or usernames and passwords. Currently I use .htaccess and HTTP Basic Authentication. If you'd like to do the same, create a folder in your root directory and name it .htaccess and in that, put the following:
AuthUserFile /path/to/.htpasswd AuthName "PSC Control Panel" AuthType Basic require valid-user
You can generate a .htpasswd file by typing the following:
$ htpasswd -c .htpasswd YOUR_USERNAME
########################################################### # # Author: Caesar "sniker" Ahlenhed # Contact: sniker@se.linux.org # Name: PSC(Prowlserverchecker, creative, I know) # Description: Reads servers listed in a database # which is controlled through a webinterface # and tries to connect to them, if # that fails, it'll send a prowl # notification that the server is down # License: GPLv2 # Url: http://sniker.codebase.nu/ # Changed: Tue Mar 15 01:23:43 CET 2011 # # # eth0 will prevail. || irc.eth0.info # ########################################################### These scripts make it possible to get alerts when servers you have added goes down, the script uses Prowl for this, so it's completely useless unless you have an iPhone with Prowl installed. What you need: 1) MySQL 2) PHP 3) Webserver 4) Python 5) Python-mysqldb(apt-get install python-mysqldb) Structure: www - Folder containing PHP-scripts. psc.py - Pything script that actually checks if the server is up psc.sql - Database structure(within mysql) README - Make a guess How to setup: Database 1) if you have done that yet, create a database. 2) import the SQL: mysql -u username -p database_name < psc.sql 3) Done! WWW 1) Move the contents of www to whatever place you desire. 2) edit lib/dbClass.php with host, username, password and database 3) Done! psc.py 1) Move the script to whatever place you desire. 2) Open up psc.py and edit the Settings section. 3) Open up crontab by typing crontab -e 4) Add the following line: */5 * * * * /path/to/psc.py >/dev/null 2>&1 5) Save. Done!
This article is pretty much redundant because the README contains most of the information you'd need. However, if you have any questions, just drop a line below.
I made this in just under 4-5 hours, it's not pretty, but it works. And I've had a lot of use for it, don't hesitate to fork it. I recommend configuring prowl like the screenshot below:
I promise you, when the emergency sound goes off, you'll shit your pants, and there's no way you can sleep through that. It's a horrible noise, hence, perfect for waking you up. Cheers!
sniker[at]codebase[dot]nu
Updated Fri, 18 Mar 2011 16:01:08 +0100