Система мониторинга: зачем?


Многие из вас, кому довелось отвечать за небольшую подсеть, сталкивались с проблемой учёта работоспособности пары десятков машин. Либо Вам просто захотелось иметь возможность в любой момент времени из любой точки планеты узнать как себя чувствует ваша торрент-качалка, оставленная включенной дома.

Лично я разрабатывал эту систему для решения проблемы скрытого удаленного наблюдения за вверенными мне компами. На этапе реализации мне предложили получить некоторый профит с этого проекта и сделать все на BASH как проект для некоторой конференции.

… и вот, разгребая хлам, я нашел исходники. Время работы веб-программистом не прошло даром, было решено полностью переверстать и расширить функционал. Собственно, начнем…

Описание платформы, зависимости


Не так давно я обновился до 12.10й версии всеми любимой Kubuntu (Ubuntu с KDE в качестве WM, GNOME не переношу). Система девственно чиста, никаких манипуляций с ней не производилось, так что в ней не будет пакетов, которых нет у Вас.
Ядро 3.5.0-21, KDE. Для работы самой системы нам понадобятся дополнительные пакеты, которые можно найти в стандартном репозитории. Набираем следующее:
sudo -s
apt-get install perl
apt-get install libnotify-bin
apt-get install lm-sensors
apt-get install sysstat
apt-get install apache2
exit

Перл нам будет нужен для выполнения скрипта-обработчика строк, libnotify — для вывода уведомления, lm-sensors — для снятия температуры, sysstat — для сбора статистики по I/O, apache2 — вебсервер для отображения. Ну, само-собой нужен любой текстовый редактор с подсветкой синтаксиса.

Реализация


Сначала я сверстал шаблон на дивах, в ряде случаев получился тизер фильма «Начало», но в целом структура дерева DOM корректна. Дальше спроектируем систему, которая будет за��олнять этот шаблон. Для этого напишем скрипт на BASH. Для этого следует знать, что для нашего (Ubuntu) дистрибутива дефолтной директорией для вебсервера апач будет /var/www. Следовательно, если кроме нашей системы ничего не будет в открытом доступе, вывод реализуем сразу в index.html.

Сам скрипт предельно простой: команда echo выводит в stdout входной параметр, мы этот выхлоп перенаправляем в файл. Так мы сможем передать в html из BASH шаблон. С точки зрения теории алгоритмов, мы получаем уже специализированный код.
#!/bin/bash
export DISPLAY=:0.0;
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<!-- Coded and designed by 19N4T0V -->
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=KOI8-R">
	<script type="text/javascript" src="jquery.js"></script>
	<title>Kansatsu</title>
	<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
	<div class="leftline">
		<a href=""><div class="header"></div></a>
		<ul class="leftmenu">
			<a href="#summary"><li>SUMMARY</li></a>
			<a href="#IO"><li>I/O STATS</li></a>
			<a href="#temperature"><li>TEMPERATURE</li></a>
			<a href="#HDD"><li>HDD</li></a>
			<a href="#RAM"><li>RAM</li></a>
			<a href="#net"><li>NETWORK</li></a>
			<a href="#PROC"><li>PROC</li></a>
		</ul>
	</div>
	<div class="content">

<!--ABOUT BEGINS HERE-->
		<div class="block">
			<div class="bl_head">
				ABOUT
			</div>
			<div class="bl_info">
				KANSATSU monitoring system is an opensource project. License is <a href="http://www.gnu.org/copyleft/"><font color="red">GNU copyleft</font></a>. This is the simple scripts written in BASH and Perl, running as a cron job . Everything you can see there is just responding to console comands. Author - <a href="http://19N4T0V.PNZ.RU/"><font color="red">19N4T0V</font></a>. If you want to receive the code to use or improve it - write me. <br>
				Thanks to <a href="http://linux.org.ru/"><font color="red">LOR</font></a> users for help and advices.<br>
				DEPENDS: lm-sensors, libnotify-bin, perl, sysstat.
			</div>
		</div>
		
<!--SUMMARY BEGINS HERE-->
		<div class="block" id = "summary">
			<div class="bl_head">
				SUMMARY
			</div>
			<div class="bl_info">
				<div class="line">
					<div class="tit">
						HOSTNAME
					</div>
					<div class="tty">' > /var/www/index.html
hostname >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						UPTIME
					</div>
					<div class="tty">' >> /var/www/index.html;
uptime | sed 's/,.*//' | sed 's/.*up //' >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						TIMESTAMP
					</div>
					<div class="tty">' >>/var/www/index.html;
date >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						KERNEL
					</div>
					<div class="tty">'>> /var/www/index.html;
uname -s >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						VERSION
					</div>
					<div class="tty">' >> /var/www/index.html;
uname -r >> /var/www/index.html;
echo '					</div>
				</div>				

				<div class="line">
					<div class="tit">
						HARDWARE
					</div>
					<div class="tty">' >> /var/www/index.html;
uname -m >> /var/www/index.html;
echo '					</div>
				</div>			

				<div class="line">
					<div class="tit">
						PROCESSOR
					</div>
					<div class="tty">' >> /var/www/index.html;
uname -p >> /var/www/index.html;
echo '					</div>
				</div>			
				
				<div class="line">
					<div class="tit">
						PLATFORM
					</div>
					<div class="tty">' >> /var/www/index.html;
uname -i >> /var/www/index.html;
echo '					</div>
				</div>					
				
				<div class="line">
					<div class="tit">
						OS
					</div>
					<div class="tty">' >> /var/www/index.html;
uname -o >> /var/www/index.html;
echo '					</div>
				</div>												
				
			</div>
		</div>
<!--IO BEGINS HERE-->	' >> /var/www/index.html;
echo '	
		<div class="block" id ="IO">
			<div class="bl_head">
				I/O STATS
			</div>
			<div class="bl_info">		' >> /var/www/index.html;
iostat -h -x > /var/www/tmp;
perl /var/www/scr.pl;
echo '					</div>
				</div>
				
			</div>
		</div>
<!--TEMPERATURE BEGINS HERE-->		
		<div class="block" id ="temperature">
			<div class="bl_head">
				TEMPERATURE
			</div>
			<div class="bl_info">
				<div class="line">
					<div class="tit">
						CHIPSET CURRENT
					</div>
					<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp1_input" | sed 's/temp1_input://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						CHIPSET CRITICAL
					</div>
					<div class="tty">' >>/var/www/index.html;
sensors -u | grep "temp1_crit" | sed 's/temp1_crit://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						CORE1 CURRENT
					</div>
					<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp2_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						CORE1 CRITICAL
					</div>
					<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp2_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						CORE2 CURRENT
					</div>
					<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp3_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo '					</div>
				</div>
				
				<div class="line">
					<div class="tit">
						CORE2 CRITICAL
					</div>
					<div class="tty">' >> /var/www/index.html;
sensors -u | grep "temp3_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
echo '					</div>
				</div>
				
			</div>
		</div>
<!--HDD BEGINS HERE-->	
		<div class="block" id="HDD">
			<div class="bl_head">
				HDD
			</div>
			<div class="bl_info">' >> /var/www/index.html;
df -a -h > /var/www/tmp;
perl /var/www/scr.pl;
echo '			</div>
		</div>
<!--RAM BEGINS HERE-->			
		<div class="block" id="RAM">
			<div class="bl_head">
				RAM
			</div>
			<div class="bl_info">' >> /var/www/index.html;
free -l -h > /var/www/tmp;
perl /var/www/scr.pl;
echo '</div></div>
<!--NETWORK BEGINS HERE-->
		<div class="block" id="net">
			<div class="bl_head">
				NETWORK
			</div>
			<div class="bl_info">' >> /var/www/index.html;
ifconfig > /var/www/tmp;
perl /var/www/scr.pl;			
echo '			</div>
		</div>
<!--PROC BEGINS HERE-->
		<div class="block" id="PROC">
			<div class="bl_head">
				PROC
			</div>
			<div class="bl_info">' >> /var/www/index.html;	
top -n1 -b > /var/www/tmp;
perl /var/www/scr.pl;
echo '			</div>
		</div>' >> /var/www/index.html;
#footer, closing "content" div and finishing DOM tree
echo '</div>
</body>
</html>' >> /var/www/index.html;
notify-send "KANSATSU" "Report has been updated";


Мы видим перенаправление потока вывода echo в изобилии. Так получается шаблон. Для заполнения шаблона используем ту же систему перенаправления потока вывода для обычных команд. Если рассмотреть подробнее, увидим следующее:
hostname >> /var/www/index.html;
uptime | sed 's/,.*//' | sed 's/.*up //' >> /var/www/index.html;
date >> /var/www/index.html;
uname -s >> /var/www/index.html;
uname -r >> /var/www/index.html;
uname -m >> /var/www/index.html;
uname -p >> /var/www/index.html;
uname -i >> /var/www/index.html;
uname -o >> /var/www/index.html;
iostat -h -x > /var/www/tmp;
perl /var/www/scr.pl;
sensors -u | grep "temp1_input" | sed 's/temp1_input://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
sensors -u | grep "temp1_crit" | sed 's/temp1_crit://' | sed 's/ *//g' | sed 's/\.000//' >> /var/www/index.html;
sensors -u | grep "temp2_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
sensors -u | grep "temp2_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
sensors -u | grep "temp3_input" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
sensors -u | grep "temp3_crit:" | sed 's/.*: //' | sed 's/.000//' >> /var/www/index.html;
df -a -h > /var/www/tmp;
perl /var/www/scr.pl;
free -l -h > /var/www/tmp;
perl /var/www/scr.pl;
ifconfig > /var/www/tmp;
perl /var/www/scr.pl;			
top -n1 -b > /var/www/tmp;
perl /var/www/scr.pl;
notify-send "KANSATSU" "Report has been updated";

Это скелет нашей системы. Можно сказать, что в таком виде — это консольная версия для liks2, например. Рассмотрим команды и дадим комментарии:
hostname возвращает имя хоста, для которого собирается информация.
uptime возвращает аптайм системы. В нашем случае он уходит по конвееру в sed, строковый редактор, где регэкспом замены из выхлопа этой команды мы выделяем лишь цифры: часы и минуты.
uname с различными параметрами — общие сведения о машине: архитектура железа, процессора, ядро, ОС…
iostat с ключом -x дает расширенное описание по работе ввода/вывода. Ключ -h обещает сделать вывод читабельным.
sensors возвращает температуру двух моих ядер и температуру матери. Ключ -u дает расширенный вывод, далее по конвееру выхлоп идет в греп, что позволяет из всего вывода выбрать одну строку с введенной подстрокой, потом в sed, где мы снова убираем лишние символы регэкспом. У меня всего три датчика, посему я вывожу 6 строк: текущие и критические температуры.
df расскажет нам о жестком диске, с флагом -a — о всех разделах, с флагом -h позволит разобрать выхлоп человеку.
free — отчетик о использовании памяти. Здесь с флагами для удобного вывода и полного отчета.
ifconfig — полный отчет о всех сетевых интерфесах. Я взял его as is.
top выводит в бесконечном цикле самые прожорливые процессы. Я использовал флаг -b для вывода в файл, что убирает спецсимволы и с флагом -n1 — указываю количество снятий показаний. Нужно отметить, что с таким набором флагов я получил список всех процессов. Если учесть, что порой надо посмотреть кто чем занят на удаленной машине — легко отслеживается по этому списку.
И в заключение notify-send дает системное сообщение об удачном обновлении отчета.

Perl-скрипт


А никто ни о чем не умолчал! Просто для этого нужна отдельная тема. Я верстал всё на дивах и не хотел использовать ни одной таблицы. В случаях, когда я получал единственное значение из утилиты, я его описание и само значение упаковывал в два дива в одну строку. Все по столбикам, все красиво. Но теперь на выходе я получаю целые таблицы. Я бы мог их сунуть в html как есть, использовав тег pre, сохранив тем самым исходное форматирование. Но в таком случае длинные таблицы будут нечитабельны, ибо для события hover дива line, в который заключается каждая строка, у меня установлена подсветка всей строки, чтобы не приходилось прик��адывать линейку к монитору или вести курсором вдоль строки до нужного столбца. Вполне лочично, что такая тенденция должна сохраняться. Для этого, собственно, я и сделал следующий ход конем: если присмотреться, везде, где используется perl, строкой выше выхлоп направлен не в html, а в /var/www/tmp. Это текстовой файл. Перл-скрипт должен построчно читать этот файл и «облачать» каждую строку в див, после чего выплёвывать это в html. Что ж, сказано — сделано:
#! perl -w

open (STREAM_IN, '/var/www/tmp');# || die "Can't open STREAM_IN\n";
open (STREAM_OUT, '>> /var/www/index.html');# || die "Can't open STREAM_OUT\n";
$s1 = '<div class="line">';
$s2 = '</div>';
while ($curr = <STREAM_IN>)
{	
	chomp($curr);
	$curr=~s/\s/\ /g;
	$out = $s1.$curr.$s2;
	print (STREAM_OUT $out);
};
close STREAM_IN;
close STREAM_OUT;


Из кода видно, что мы берем строку, отсекаем символ перевода строки, меняем пробелы на коды (консолью таблица формируется именно пробелами, если дать их в html как есть — будет каша). Статически задаем вспомогательные строки и конкатенацией получаем одну строку, которая дописывается в конец html-отчета. Из кода скрипта на BASH видно, что следом за подобными конструкциями следует echo, которое спешит закрыть дивы.

Такой скрипт работает с любым содержимым одного и того же файла и дозаписывает его в конец другого файла. Я всегда отправляю вывод таблицы во временный файл на ПЕРЕ запись, он сам очищается, потом вызывается скрипт, который этот вывод приводит к общему шаблону и ДОзаписывает к html. Один скрипт на все времена).

Дополнительные файлы, последние штрихи


Совершенно очевидно, что нужно написать таблицу стилей. Я преследовал принцип KISS, хотел максимум функциональности и минимум загруженности. Выбрал следующее решение: слева фиксированно будут ссылки на каждый блок отчета, а сам отчет будет листаться справа.

html 
{
	height:100%; 
	width:100%
}

body
{
	height:100%; 
	width:100%; 
	padding:0px; 
	margin:0; 
	color:#000; 
	font-family:Arial; 
	font-size:14px; 
	background-color:#FFF;
}

.leftline
{
	width:210px;
	overflow:hidden;
	float:left;
	height:100%;
	position:fixed;
	top:0px;
	left:0px;
}

.header
{
	width:100%;
	float:none;
	background:url(logo.png) top left no-repeat;
	height:160px;
	
}

.leftmenu 
{
	padding:0px;
}

.leftmenu li
{
	width:200px; 
	height:30px; 
	background:#000; 
	font-size:24px; 
	line-height:1.3;
	float:none; 
	margin:10px 10px 10px 0px ; 
	text-align:right; 
	padding-right:10px; 
	list-style:none
}

.leftmenu li:hover
{
	background:#FF0000;
}

a
{
	color:#FFF;
	text-decoration:none;
}

.content
{
	
}

.block
{
	float:none;
	margin-top:20px;
	margin-left:250px;
	margin-right:50px;
	border-left:2px black solid;
}

.bl_head
{
	color:#fff;
	background:#000;
	padding:10px 0px 10px 10px;
}



.bl_info
{
	color:#000;
	padding-left:10px;
	padding-top:5px;
}

.line
{
	display:block;
	height:20px;
	cursor:default;
}

.line:hover
{
	background:red;
}

.tit
{
	width:200px;
	float:left;
}

.tty
{
	
}



По коду — это всё. Нужно лишь еще 2 файла и установить права. Первый файл — это, собственно, логотип. Полчаса GIMP'a и вуаля! Второй файл — тот самый временный. У меня он назван tmp. Всё это я бросил в одну большую кучу в директорию /var/www. Далее установил права на файлы. Пока эта версия находится в отладке, я решил сделать очешь щедрый поступок:
sudo chmod 777 /var/www/index.html
sudo chmod 777 /var/www/tmp
sudo chmod 777 /var/www/scr.pl
sudo chmod 777 /var/www/style.css

Собственно, сисиема готова к использованию. Но есть одно но: обновлять придется руками. Для отладки я прописал в башрц новый алиас. Саму систему я назвал Kansatsu, что означает «наблюдение». Посему выглядит алиас следующим образом:
alias kansatsu="sh /var/www/kansatsu.sh"


Автоматизация


Не дело иметь под рукой такую мощную ОСь и делать что-то руками. Здесь нас выручает старый добрый крон.
crontab -e


Как видно, я поставил обновление на каждые пять минут. Благодаря libnotify я получаю отчеты:

Веб-интерфейс же выглядит следующим образом:

Всё как и описывал: фиксированная панель слева, остальное с маргином на правый бок динамически масштабируется, информация разбита по блокам, подсветка строк.
Проект опенсурсный, если есть желание — улучшайте, используйте, славьте РМС.