├── .gitignore ├── config.php.example ├── dashboard.css ├── README.markdown ├── default.css └── index.php /.gitignore: -------------------------------------------------------------------------------- 1 | status.dat* 2 | config.php 3 | *.swp 4 | *.swo 5 | -------------------------------------------------------------------------------- /config.php.example: -------------------------------------------------------------------------------- 1 | seconds ago 23 | // Set this to a higher value then status_update_interval in your 24 | // nagios.cfg 25 | // $statusFileTimeout = 60; 26 | 27 | // Filter hosts - filter hosts with (only machines starting with 'brno-') 28 | // $hostFilter = function ($match) { return preg_match('/^brno-/', $match); }; 29 | 30 | 31 | // Enable fortune output 32 | // $enableFortune = false; 33 | // $fortunePath = "/usr/games/fortune"; 34 | 35 | // Set to callable to write custom heading by function 36 | // $nagliteHeading = function() { echo("

This is Naglite3

\n"); }; 37 | // Set to string to write the string 38 | // $nagliteHeading = ''; 39 | // 40 | -------------------------------------------------------------------------------- /dashboard.css: -------------------------------------------------------------------------------- 1 | div.statusFileState { 2 | position: absolute; 3 | top: 0; 4 | width: 100%; 5 | font-size: 18pt; 6 | padding: 10px 0; 7 | font-weight: bold; 8 | } 9 | 10 | div.fortune { 11 | display: none; 12 | } 13 | 14 | div.stat.warning, div.stat.acknowledged { 15 | color: #000; 16 | } 17 | 18 | #content { 19 | margin-top: 30px; 20 | } 21 | 22 | div.section { 23 | margin: 0; 24 | } 25 | 26 | #services, #hosts { 27 | padding: 20px 0 10px 0; 28 | font-size: 24pt; 29 | } 30 | 31 | #hosts { 32 | padding-top: 40px; 33 | } 34 | 35 | #services h2, #hosts h2 { 36 | float: left; 37 | } 38 | 39 | #services div.stats, #hosts div.stats { 40 | display: none; 41 | } 42 | 43 | #content div.state { 44 | font-size: 24pt; 45 | border-left: 10px solid #000; 46 | border-right: 10px solid #000; 47 | box-sizing: border-box; 48 | } 49 | 50 | table { 51 | margin: 0; 52 | } 53 | 54 | table tbody { 55 | margin: 0; 56 | width: 100%; 57 | padding: 0 20px; 58 | } 59 | 60 | table tbody tr:first-child { 61 | display: none; 62 | } 63 | 64 | h3 { 65 | display: none; 66 | } 67 | 68 | table tbody tr.acknowledged { 69 | display: none; 70 | } 71 | 72 | table tbody tr { 73 | display: block; 74 | float: left; 75 | width: 50%; 76 | border: 10px solid #000; 77 | box-sizing: border-box; 78 | } 79 | 80 | table tbody tr td { 81 | display: block; 82 | width: 100%; 83 | } 84 | 85 | table tbody tr td.hostname { 86 | font-size: 20pt; 87 | background: transparent; 88 | font-weight: bold; 89 | } 90 | 91 | table tbody tr td.service { 92 | font-size: 32pt; 93 | font-weight: bold; 94 | } 95 | 96 | table tbody tr td.state { 97 | display: none; 98 | } 99 | 100 | table tbody tr td.duration, table tbody tr td.attempts { 101 | font-size: 16pt; 102 | } 103 | 104 | table tbody tr td.output { 105 | font-size: 14pt; 106 | text-align: center; 107 | } 108 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Naglite3 2 | ======== 3 | 4 | Nagios/Icinga status monitor for a NOC or operations room. 5 | 6 | Inspired by Naglite (http://www.monitoringexchange.org/inventory/Utilities/AddOn-Projects/Frontends/NagLite) 7 | and Naglite2 (http://laur.ie/blog/2010/03/naglite2-finally-released/). 8 | 9 | Written by Steffen Zieger . 10 | Licensed under the GPL. 11 | 12 | In case of any problems or bug fixes, feel free to contact me. 13 | 14 | Requirements 15 | ------------ 16 | 17 | Naglite3 is only tested with Nagios3, but it should also work with Nagios2. 18 | If you're running Nagios2, please let me know. 19 | 20 | [nkadel](https://github.com/nkadel) has reported, that it's also working with Icinga. 21 | 22 | - Web server of your choice with PHP support 23 | - PHP 5.2 or newer 24 | - git 25 | 26 | Naglite3 must be installed on the same host where Nagios is running, as it 27 | needs to read status.dat from Nagios. 28 | 29 | Installation 30 | ------------ 31 | 32 | 1. Switch to a directory accessible through your web server (e.g. /var/www/). 33 | 2. git clone git://github.com/saz/Naglite3.git 34 | 3. Copy config.php.example to config.php if you need to change a setting. 35 | 4. Open a browser and point it to your Naglite3 installation. 36 | 37 | Customization 38 | ------------- 39 | 40 | For all possible config options have a look at config.php.example 41 | 42 | ### CSS 43 | 44 | If you want to change colors, create a file called 'custom.css' in the 45 | directory where Naglite3 is placed and add your changes. 46 | 47 | If you want to use a per site css, just pass the GET-parameter "css" pointing to a local file. 48 | e.g. http://your-host/Naglite3/?css=my_custom_css 49 | 50 | #### Dashboard CSS 51 | 52 | To show the naglite screen in a manner that's readable from a few feet away, you can use the builtin dashboard stylesheet. 53 | e.g. http://your-host/Naglite3/?css=dashboard 54 | 55 | ### Refresh interval 56 | 57 | You can change the refresh interval (in seconds) through a GET parameter, too. 58 | 59 | Example: 60 | http://your-host/Naglite3/?refresh=100 61 | -------------------------------------------------------------------------------- /default.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0px; 3 | margin: 0px; 4 | } 5 | 6 | html, body { height: 99%; } 7 | 8 | body { 9 | font-family: Verdana, Tahoma, Helvetica, Arial, sans-serif; 10 | background-color: #000000; 11 | font-size: 14px; 12 | text-align: center; 13 | } 14 | 15 | table { 16 | width: 100%; 17 | border: none; 18 | border-spacing: 2px; 19 | white-space: nowrap; 20 | } 21 | 22 | th, td { padding: 2px 10px; } 23 | th, td.hostname, td.address { background-color: #d3d3d3; } 24 | 25 | .output { 26 | white-space: normal; 27 | text-align: left; 28 | } 29 | 30 | .subhosts { 31 | margin: 2px; 32 | padding: 1px; 33 | font-size: 12px; 34 | text-align: left; 35 | } 36 | 37 | h1 { 38 | text-align: center; 39 | font-weight: bold; 40 | color: #ffffff; 41 | } 42 | 43 | h2.title { 44 | text-align: left; 45 | font-weight: bold; 46 | padding-bottom: 5px; 47 | font-size: 1.2em; 48 | } 49 | 50 | h3.title { 51 | text-align: left; 52 | font-weight: bold; 53 | padding: 10px 2px 0px 2px; 54 | font-size: 12px; 55 | color: #ffffff; 56 | } 57 | 58 | #content { 59 | height: 97.0%; 60 | margin: 10px; 61 | } 62 | 63 | .section { 64 | color: #ffffff; 65 | font-weight: bold; 66 | overflow: auto; 67 | padding: 20px 2px 0px 2px; 68 | margin-top: 10px; 69 | } 70 | 71 | .subsection { font-size: 12px; } 72 | 73 | .stats { 74 | float: right; 75 | margin-top: -2em; 76 | } 77 | 78 | .stat { 79 | display: inline; 80 | padding: 3px; 81 | margin-left: 10px; 82 | } 83 | 84 | div.state { 85 | font-size: 20px; 86 | font-weight: bold; 87 | color: #ffffff; 88 | margin: 0px 2px 3px 2px; 89 | padding: 3px; 90 | } 91 | 92 | .up, .ok { background-color: green; } 93 | .warning { background-color: yellow; } 94 | .critical, .down { background-color: red; } 95 | .pending { background-color: #488acf; } 96 | .unknown { background-color: #00ffff; } 97 | .notification { background-color: #69b3b3; } 98 | .acknowledged { background-color: #d3d3d3; } 99 | .unreachable { background-color: orange; } 100 | 101 | .fortune { 102 | margin-top: 10px; 103 | color: #ffffff; 104 | } 105 | 106 | .statusFileState { 107 | color: #ffffff; 108 | } 109 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 8 | * @version 1.6 9 | * @license GPL 10 | **/ 11 | 12 | /** 13 | * 14 | * Please do not change values below, as this will make it harder 15 | * for you to update in the future. 16 | * Rename config.php.example to config.php and change the values there. 17 | * 18 | **/ 19 | 20 | // Set file path to your nagios status log 21 | $statusFile = '/var/cache/nagios3/status.dat'; 22 | 23 | // Objects file 24 | $objectsFile = '/var/cache/icinga/objects.cache'; 25 | 26 | // Default refresh time in seconds 27 | $refresh = 10; 28 | 29 | // Show warning state if status file was last updated seconds ago 30 | // Set this to a higher value then status_update_interval in your nagios.cfg 31 | $statusFileTimeout = 60; 32 | 33 | 34 | $hostFilter = function ($match) { return TRUE; }; 35 | 36 | // Enable fortune output 37 | $enableFortune = false; 38 | $fortunePath = "/usr/games/fortune"; 39 | 40 | // Uncomment to show custom heading 41 | //$nagliteHeading = ''; 42 | 43 | // Show IP addresses of hosts 44 | $showAddresses = FALSE; 45 | 46 | 47 | /* 48 | * Nothing to change below 49 | */ 50 | 51 | // If there is a config file, require it to overwrite some values 52 | $config = 'config.php'; 53 | if (file_exists($config)) { 54 | require $config; 55 | } 56 | 57 | // Disable E_NOTICE error reporting 58 | $errorReporting = error_reporting(); 59 | if ($errorReporting & E_NOTICE) { 60 | error_reporting($errorReporting ^ E_NOTICE); 61 | } 62 | 63 | // Disable caching and set refresh interval 64 | header("Pragma: no-cache"); 65 | if (!empty($_GET["refresh"]) && is_numeric($_GET["refresh"])) { 66 | $refresh = $_GET["refresh"]; 67 | } 68 | header("Refresh: " .$refresh); 69 | 70 | // Nagios Status Map 71 | $nagios["host"]["ok"] = 0; 72 | $nagios["host"]["down"] = 1; 73 | $nagios["host"]["unreachable"] = 2; 74 | $nagios["host"] += array_keys($nagios["host"]); 75 | $nagios["service"]["ok"] = 0; 76 | $nagios["service"]["warning"] = 1; 77 | $nagios["service"]["critical"] = 2; 78 | $nagios["service"]["unknown"] = 3; 79 | $nagios["service"] += array_keys($nagios["service"]); 80 | 81 | /** 82 | * 83 | * Functions 84 | * 85 | **/ 86 | 87 | function duration($end) { 88 | $DAY = 86400; 89 | $HOUR = 3600; 90 | 91 | $now = time(); 92 | $diff = $now - $end; 93 | $days = floor($diff / $DAY); 94 | $hours = floor(($diff % $DAY) / $HOUR); 95 | $minutes = floor((($diff % $DAY) % $HOUR) / 60); 96 | $secs = $diff % 60; 97 | return sprintf("%dd, %02d:%02d:%02d", $days, $hours, $minutes, $secs); 98 | } 99 | 100 | function serviceTable($nagios, $services, $hostInfo, $select = false, $type = false) { 101 | if (false === $type) { 102 | print("\n"); 103 | } else { 104 | print(sprintf("
\n", $type)); 105 | } 106 | $addressColumn = empty($hostInfo)?'':''; 107 | print("$addressColumn\n"); 108 | print(""); 109 | 110 | foreach ($select as $selectedType) { 111 | if ($services[$selectedType]) { 112 | foreach ($services[$selectedType] as $service) { 113 | $state = $nagios["service"][$service["current_state"]]; 114 | if (false === $type) { 115 | $rowType = $state; 116 | } else { 117 | $rowType = $type; 118 | if ("acknowledged" !== $type) { 119 | $state = $type; 120 | } 121 | } 122 | print(sprintf("\n", $rowType)); 123 | if ($hostInfo) { 124 | $hostName = $service["host_name"]; 125 | print("\n"); 126 | } else { 127 | print(sprintf("\n", $service['host_name'])); 128 | } 129 | print(sprintf("\n", $service['service_description'])); 130 | print(sprintf("\n"); 135 | print(sprintf("\n", duration($service['last_state_change']))); 136 | print(sprintf("\n", $service['current_attempt'], $service['max_attempts'])); 137 | print(sprintf("\n", strip_tags($service['plugin_output'], ''))); 138 | print("\n"); 139 | } 140 | } 141 | } 142 | print("
AddressHostServiceStatusDurationAttemptsPlugin Output
$hostName{$hostInfo[$hostName]["address"]}%s%s%s", $state)); 131 | if ($service["current_attempt"] < $service["max_attempts"]) { 132 | print(" (Soft)"); 133 | } 134 | print("%s%s/%s%s
\n"); 143 | } 144 | 145 | function sectionHeader($type, $counter) { 146 | print(sprintf('
', $type)); 147 | print(sprintf('

%s Status

', ucfirst($type))); 148 | print('
'); 149 | foreach($counter[$type] as $type => $value) { 150 | print(sprintf('
%s %s
', $type, $value, ucfirst($type))); 151 | } 152 | print('
'); 153 | } 154 | 155 | /** 156 | * 157 | * Parse Nagios status 158 | * 159 | **/ 160 | 161 | // Check if status file is readable 162 | if (!is_readable($statusFile)) { 163 | die("Failed to read nagios status from '$statusFile'"); 164 | } 165 | 166 | 167 | $statusFileMtime = filemtime($statusFile); 168 | $statusFileState = 'ok'; 169 | if ((time() - $statusFileMtime) > $statusFileTimeout) { 170 | $statusFileState = 'critical'; 171 | } 172 | 173 | $nagiosStatus = file($statusFile); 174 | $in = false; 175 | $type = "unknown"; 176 | $status = array(); 177 | $host = null; 178 | 179 | $lineCount = count($nagiosStatus); 180 | for($i = 0; $i < $lineCount; $i++) { 181 | if(false === $in) { 182 | preg_match('/(info|programstatus|hoststatus|servicestatus|servicecomment) {/', trim($nagiosStatus[$i]), $matches); 183 | if ($matches) { 184 | $in = true; 185 | $type = $matches[1]; 186 | if(!empty($status[$type])) { 187 | $arrPos = count($status[$type]); 188 | } else { 189 | $arrPos = 0; 190 | } 191 | continue; 192 | } 193 | } else { 194 | $pos = strpos($nagiosStatus[$i], "}"); 195 | if(false !== $pos && strlen(trim($nagiosStatus[$i])) == 1) { 196 | $in = false; 197 | $type = "unknown"; 198 | continue; 199 | } 200 | 201 | // Line with data found 202 | list($key, $value) = explode("=", trim($nagiosStatus[$i]), 2); 203 | if("hoststatus" === $type) { 204 | if("host_name" === $key) { 205 | $host = $value; 206 | } 207 | $status[$type][$host][$key] = $value; 208 | } else { 209 | $status[$type][$arrPos][$key] = $value; 210 | } 211 | } 212 | } 213 | 214 | /* drop unwanted status entries 215 | */ 216 | foreach (array_keys($status) as $a_type) { 217 | foreach ($status[$a_type] as $a_key => $a_value) { 218 | if (!array_key_exists('host_name', $a_value)) 219 | continue; 220 | if (!$hostFilter($a_value['host_name'])) { 221 | unset($status[$a_type][$a_key]); 222 | } 223 | } 224 | } 225 | /** 226 | * 227 | * Parse Nagios objects cache 228 | * 229 | **/ 230 | if ($objectsFile and !is_readable($objectsFile)) { 231 | die("Failed to read objects file from '$objectsFile'"); 232 | } 233 | 234 | $hostInfo = array(); 235 | if ($objectsFile and $showAddresses) { 236 | $nagiosObjects = file($objectsFile); 237 | $in = false; 238 | $type = null; 239 | $host = null; 240 | $lineCount = count($nagiosObjects); 241 | for($i = 0; $i < $lineCount; $i++) { 242 | if(false === $in) { 243 | $pos = strpos($nagiosObjects[$i], "{"); 244 | if (false !== $pos) { 245 | $in = true; 246 | $type = trim(substr($nagiosObjects[$i], 6, $pos-1-6)); 247 | continue; 248 | } 249 | } else { 250 | $pos = strpos($nagiosObjects[$i], "}"); 251 | if(false !== $pos) { 252 | $in = false; 253 | $type = "unknown"; 254 | continue; 255 | } 256 | 257 | // Line with data found 258 | list($key, $value) = explode("\t", trim($nagiosObjects[$i]), 2); 259 | if("host" === $type) { 260 | if("host_name" === $key) { 261 | $host = $value; 262 | $hostInfo[$host] = array(); 263 | } else { 264 | $hostInfo[$host][$key] = $value; 265 | } 266 | } 267 | } 268 | } 269 | } 270 | 271 | 272 | // Initialize some variables 273 | $counter = array(); 274 | $states = array(); 275 | $hosts = array(); 276 | 277 | foreach (array_keys($status) as $type) { 278 | switch ($type) { 279 | case "hoststatus": 280 | $hosts = $status[$type]; 281 | foreach ($hosts as $host) { 282 | if ((int)$host['scheduled_downtime_depth'] > 0) { 283 | continue; 284 | } else if ($host['problem_has_been_acknowledged'] == '1') { 285 | $counter['hosts']['acknowledged']++; 286 | $states['hosts']['acknowledged'][] = $host['host_name']; 287 | } else if ($host['notifications_enabled'] == 0) { 288 | $counter['hosts']['notification']++; 289 | $states['hosts']['notification'][] = $host['host_name']; 290 | } else if ($host['has_been_checked'] == 0) { 291 | $counter['hosts']['pending']++; 292 | $states['hosts']['pending'][] = $host['host_name']; 293 | } else { 294 | switch ($host['current_state']) { 295 | case $nagios['host']['ok']: 296 | $counter['hosts']['ok']++; 297 | break; 298 | case $nagios['host']['down']: 299 | $counter['hosts']['down']++; 300 | $states['hosts']['down'][] = $host; 301 | break; 302 | case $nagios['host']['unreachable']: 303 | $counter['hosts']['unreachable']++; 304 | $states['hosts']['unreachable'][] = $host['host_name']; 305 | break; 306 | } 307 | } 308 | } 309 | break; 310 | 311 | case "servicestatus": 312 | $services = $status[$type]; 313 | foreach ($services as $service) { 314 | // Ignore all services if host state is not ok 315 | $state = $status['hoststatus'][$service['host_name']]['current_state']; 316 | if ($nagios['host']['ok'] != $state) { 317 | continue; 318 | } 319 | 320 | if ((int)$service['scheduled_downtime_depth'] > 0) { 321 | continue; 322 | } else if ($service['problem_has_been_acknowledged'] == '1') { 323 | $counter['services']['acknowledged']++; 324 | $states['services']['acknowledged'][] = $service; 325 | } else if ($service['notifications_enabled'] == '0') { 326 | $counter['services']['notification']++; 327 | $states['services']['notification'][] = $service; 328 | } else if ($service['has_been_checked'] == '0') { 329 | $counter['services']['pending']++; 330 | $states['services']['pending'][] = $service; 331 | } else { 332 | switch ($service['current_state']) { 333 | case $nagios['service']['ok']: 334 | $counter['services']['ok']++; 335 | break; 336 | case $nagios['service']['warning']: 337 | $counter['services']['warning']++; 338 | $states['services']['warning'][] = $service; 339 | break; 340 | case $nagios['service']['critical']: 341 | $counter['services']['critical']++; 342 | $states['services']['critical'][] = $service; 343 | break; 344 | case $nagios['service']['unknown']: 345 | $counter['services']['unknown']++; 346 | $states['services']['unknown'][] = $service; 347 | break; 348 | } 349 | } 350 | } 351 | break; 352 | } 353 | } 354 | 355 | /** 356 | * 357 | * Status output 358 | * 359 | **/ 360 | 361 | echo "\n"; 363 | echo "\n"; 364 | echo "\n"; 365 | echo " Nagios Monitoring System - Naglite3\n"; 366 | //echo " \"\n"; 367 | echo " \n"; 368 | echo " \n"; 369 | if (is_readable("custom.css")) { 370 | echo " \n"; 371 | } 372 | if (isset($_GET['css']) && is_readable(basename($_GET['css']) . '.css')) { 373 | echo " \n"; 374 | } 375 | echo "\n"; 376 | echo "\n"; 377 | 378 | print("\n"); 379 | print(sprintf('
', $statusFileState)); 380 | print(sprintf('Status file last updated at %s', date(DATE_RFC2822, $statusFileMtime))); 381 | print("
\n"); 382 | 383 | echo "\n"; 388 | echo '
'; 389 | if(is_callable($nagliteHeading)) { 390 | $nagliteHeading(); 391 | } elseif ($nagliteHeading) { 392 | echo '

'.$nagliteHeading.'

'; 393 | } 394 | 395 | sectionHeader('hosts', $counter); 396 | if (!$showAddresses) 397 | $hostInfo = False; 398 | $addressColumn = empty($hostInfo)?'':'Address'; 399 | 400 | if ($counter['hosts']['down']) { 401 | echo ""; 402 | echo "$addressColumn"; 403 | foreach($states['hosts']['down'] as $host) { 404 | $state = $nagios["host"][$host["current_state"]]; 405 | echo "\n"; 406 | if ($showAddresses) { 407 | $hostName = $host["host_name"]; 408 | echo "\n"; 409 | } else { 410 | echo "\n"; 411 | } 412 | echo "\n"; 413 | echo "\n"; 414 | print(sprintf("\n", htmlspecialchars($host['plugin_output']))); 415 | echo "\n"; 416 | } 417 | echo "
HostStatusDurationStatus Information
$hostName{$hostInfo[$hostName]["address"]}{$host["host_name"]}{$state}".duration($host["last_state_change"])."%s
"; 418 | } else { 419 | echo "
ALL MONITORED HOSTS UP
\n"; 420 | } 421 | 422 | foreach(array('unreachable', 'acknowledged', 'pending', 'notification') as $type) { 423 | if ($counter['hosts'][$type]) { 424 | print(sprintf('
%s: %s
', $type, ucfirst($type), implode(', ', $states['hosts'][$type]))); 425 | } 426 | } 427 | 428 | sectionHeader('services', $counter); 429 | 430 | if ($counter['services']['warning'] || $counter['services']['critical'] || $counter['services']['unknown']) { 431 | serviceTable($nagios, $states['services'], $hostInfo, array('critical', 'warning', 'unknown')); 432 | } else { 433 | print("
ALL MONITORED SERVICES OK
\n"); 434 | } 435 | 436 | foreach(array('acknowledged', 'notification', 'pending') as $type) { 437 | if ($counter['services'][$type]) { 438 | print(sprintf('

%s

', ucfirst($type))); 439 | print('
'); 440 | serviceTable($nagios, $states['services'], $hostInfo, array($type), $type); 441 | print('
'); 442 | } 443 | } 444 | 445 | if($enableFortune === true) { 446 | echo "
"; 447 | print(shell_exec($fortunePath)); 448 | echo "
"; 449 | } 450 | 451 | print("\n"); 452 | print("\n"); 453 | --------------------------------------------------------------------------------