Заголовок сообщения: Статистика при помощи snmp+mrtg+rrdtool

Прошу не пинать ногами. Естественно, есть более простые способы по формированию статистики. Если есть предложение и коментарии, то вперед.

Начнем с того, что клиенты часто просят помочь настроить статистику на FreeBSD. И задача в основном сводится к тому, чтобы посчитать все с точностью до мегабайта. Для "гуру" естественно не составит труда снять статистику с того же ipfw, записать данные в базу, сделать выборку из нее и выдать клиенту через веб. Но согласитесь, не совсем это простой путь и ко всему нужно вешать слишком много дополнительных сервисов. Для того чтобы как-то облегчить жизнь админам и конечным пользователям, я и решил написать сюда как легче всего организовать подсчет статистики.

Нам понадобиться только несколько готовых решений.
Во-первых, вебсервер Apache в связке с PHP.
Во-вторых, MRTG последней версии. И от того же производителя чудный rrdtool.
В-третьих, то, с помощью чего мы будем собирать статистику -- net-snmp.
Итак, сбор статистики подразумевает, что существует постоянное подключение к internet, поэтому первое, что нужно сделать, это обновить порты.



Думаю, не стоит описывать как править ports-supfile. Единственное, что хочу посоветовать -- выбирайте быстрейшее для себя зеркало. Для меня это cvsup.ru.freebsd.org, но он постоянно забит, поэтому я пользуюсь cvsup5.ru.FreeBSD.org, который никогда не бывает занят.
Ждем пока обновяться порты.
После этого, если не установлен Apache, приступаем к его сборке, установке и конфигурированию. Я это все описывать не буду, так как есть для этого свой раздел на форуме и масса информации по всему интернету. После того, как он будет настроен, оставим его в покое на какое-то время и перейдем к сборке net-snmp:



Затем, чтобы не сильно утруждать себя его конфигурированием, могу посоветовать сделать следущее:



и вписываем в конфиг следущие строки



Если кого то напрягает ee, воспользуйтесь vi, но для новичков лучше будет ee. Нажимаем Esc, сохраняем сделанные изменения. Вообщем-то все. С snmpd мы справились, осталось его запустить и сделать так, чтобы он после перезагрузки мог функционировать нормально. Для этого проделываем следущее:



и добавляем в него строку



После этого запускаем сервис:



и смотрим, есть ли snmpd в списке выполняемых процессов:



Видим, что snmpd запущен. Запоминаем, какую вы прописали community и приступаем к следующему этапу -- сборке mrtg.



затем



После того как все собрано и установлено приступаем к правке конфигов.

Cразу подумаем, куда мы будем складывать логи. Я предпочитаю /var/spool/mrtg, так что



и правим права на доступ



далее идем и правим конфиг mrtg



Нам нужно исправить Global Config Options на следующее:



Сохраним файл и приступим собственно к созданию конфига для учета статистики.
Для создания простого конфига существует утилита cfgmaker. Допустим ваш сервер имеет внутренний IP 192.168.0.1, тогда делаем следущее



Будет создан необходимый конфиг. Открываем его и проверяем; если все в порядке, то открываем файл mrtg.cfg и вписываем в него следущее

Далее добавляем в cron следущее


Перестартовываем cron, ждем 5 минут, идем в /var/spool/mrtg и смотрим что там есть, если появились файлы, то все ворядке идет опрос, статистика считается и остается только одно сделать выборку из rrd файлов и выложить это в веб для наглядности.

Теперь возвращаемся к вебсерверу. Те, кому не лень, могут сделать виртуальный хост, чтобы было что-то вроде stats.myhost.ru. Кому лень, могут просто сделает папку на сушествующем сервере и создать в этой папке пустой файл index.php.
Как-то я наткнулся на замечательный скрипт, который делает выборку из rrd файлов. К сожалению ни страницу автора, ни самого автора я не запомнил, так что если кто-то знает его, пусть добавит авторство. Скрипт из себя представляет следущее.

Код:
<?php

/* The directory where the rrd files are located */
$dir = '/var/spool/mrtg';


/* List all devices that MRTS should'n display, */
$exclude = array('secret', 'topsecret');


/* RRDtool path - where are the the executable located */
$rrdcommand = '/usr/local/bin/rrdtool';


/* Change this to get another top on the site */
function top($name)
{ ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <title><?php echo $GLOBALS['title']; ?></title>
  <style type="text/css">
   <!--

   .datahead,.total1head,.total2head,.data,.total1,.total2
   {
         border: 1px solid black;
     width: 50px;
   }

   .datahead,.total1head,.total2head
   {
    font-size: 12px;
    font-weight: bold;
   }

   .data,.total1,.total2
   {
    font-size: 10px;
   }

   .total1,.total1head
   {
    background-color: #dddddd;
   }

   .total2,.total2head
   {
    background-color: #bbbbbb;
   }

   h1,h2,h3
   {
    text-align: center;
   }

   -->
  </style>
 </head>
 <body>
 <h1><?php echo $GLOBALS['title']; ?></h1>
 <h2><?php echo $name; ?></h2>
<?php }



/* Change this to get another bottom on the site */
function bottom()
{ ?>
  <hr>
  Created by <a href="??????"><?php echo $GLOBALS['title'] ?></a>
 </body>
</html>
<?php }


/***************************************************************************\
*                                 Variables                                 *
\***************************************************************************/


        /* File extension of the MRTG-RRD-files */
        $extension = '.rrd';

        /* This version */
        $version = 'ru';

        /* The title */
        $title = "me.host.$version";


/***************************************************************************\
*                                 Functions                                 *
\***************************************************************************/

        /* Checks if a name is a valid device name */
        function validname($name)
        {
                return in_array($name, $GLOBALS['legalnames']);
        } //function validname($name)


        /* Convert a device name to a file name */
        function filename($name)
        {
                return $GLOBALS['dir'].'/'.$name.$GLOBALS['extension'];
        } //function filename($name)


        /* Formats a number with KB, MB etc. */
        function humanreadable($size)
        {
                $names = array('B', 'KB', 'MB', 'GB', 'TB');
                $times = 0;
                while($size>1024)
                {
                        $size = round(($size*100)/1024)/100;
                        $times++;
                }
                return "$size " . $names[$times];
        } //function humanreadable($size)


        /* Convert a month number to a month name */
        function monthname($no)
        {
                $names = array(1 => 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
                return $names[$no];
        } //function monthname($no)


        /* Convert year and month number to a string useful for rrdtool  */
        function monthstartend($year, $month)
        {
                $start = mktime(0, 0, 0, $month, 1, $year);
                if($month==12)
                        $end = mktime(0, 0, 0, 1, 1, $year+1);
                else
                        $end = mktime(0, 0, 0, $month+1, 1, $year);
                return " -s $start -e $end ";
        }


        /* Output HTML for a year */
        function showyear($year, $months)
        {

                $sumyear = array();
                $sumquater = array();

                printf("<h3>Year: %s</h3>\n", $year);
                printf("<table><tr><td></td>\n");


                for($quater=1; $quater<=4; $quater++)
                {
                        /* month */
                        for($i=($quater-1)*3+1; $i<=($quater-1)*3+3; $i++)
                        {
                                printf("<td class=\"datahead\"><a href=\"%s?name=%s&amp;year=%s&amp;month=%s\">%s (%s)</a></td>\n", $_SERVER['SCRIPT_NAME'], $_GET['name'], $year, $i, monthname($i), $i);
                        }
                        /* quater */
                        printf("<td class=\"total1head\">Quater %s</td>\n", $quater);
                }
                /* year */
                printf("<td class=\"total2head\">Year</td>\n");


                printf("</tr><tr><td class=\"datahead\">In</td>\n");


                for($quater=1; $quater<=4; $quater++)
                {
                        /* month */
                        for($i=($quater-1)*3+1; $i<=($quater-1)*3+3; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable($months[$i]['in']));
                                $sumyear['in'] += $months[$i]['in'];
                                $sumquater[$quater]['in'] += $months[$i]['in'];
                        }
                        /* quater */
                        printf("<td class=\"total1\">%s</td>\n", humanreadable($sumquater[$quater]['in']));
                }
                /* year */
                printf("<td class=\"total2\">%s</td>\n", humanreadable($sumyear['in']));


                printf("</tr><tr><td class=\"datahead\">Out</td>\n");


                for($quater=1; $quater<=4; $quater++)
                {
                        /* month */
                        for($i=($quater-1)*3+1; $i<=($quater-1)*3+3; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable($months[$i]['out']));
                                $sumyear['out'] += $months[$i]['out'];
                                $sumquater[$quater]['out'] += $months[$i]['out'];
                        }
                        /* quater */
                        printf("<td class=\"total1\">%s</td>\n", humanreadable($sumquater[$quater]['out']));
                }
                /* year */
                printf("<td class=\"total2\">%s</td>\n", humanreadable($sumyear['out']));


                printf("</tr><tr><td class=\"datahead\">Max</td>\n");


                for($quater=1; $quater<=4; $quater++)
                {
                        /* month */
                        for($i=($quater-1)*3+1; $i<=($quater-1)*3+3; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable(max($months[$i]['in'], $months[$i]['out'])));
                        }
                        /* quater */
                        printf("<td class=\"total1\">%s</td>\n", humanreadable(max($sumquater[$quater]['in'], $sumquater[$quater]['out'])));
                }
                /* year */
                printf("<td class=\"total2\">%s</td>\n", humanreadable(max($sumyear['in'], $sumyear['out'])));


                printf("</tr><tr><td class=\"datahead\">Sum</td>\n");


                for($quater=1; $quater<=4; $quater++)
                {
                        /* month */
                        for($i=($quater-1)*3+1; $i<=($quater-1)*3+3; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable($months[$i]['in'] + $months[$i]['out']));
                        }
                        /* quater */
                        printf("<td class=\"total1\">%s</td>\n", humanreadable($sumquater[$quater]['in'] + $sumquater[$quater]['out']));
                }
                /* year */
                printf("<td class=\"total2\">%s</td>\n", humanreadable($sumyear['in'] + $sumyear['out']));


                printf("</tr></table>\n");


        } //function showyear($year, $months)


        /* Output HTML for a month */
        function showmonth($year, $month, $days)
        {

                $summonth = array();
                $daysinmonth = date("t", mktime(0, 0, 0, $month, 1, $year));

                printf("<h3>Month: %s %s</h3>\n", $year, monthname($month));

                for($j=1; $j<=2; $j++)
                {
                       
                        if($j==1)
                        {
                                $start = 1;
                                $end = 16;
                        }
                        else
                        {
                                $start = 17;
                                $end = $daysinmonth;
                        }
                       
                        printf("<table><tr><td></td>\n");

                        for($i=$start; $i<=$end; $i++)
                        {
                                printf("<td class=\"datahead\">%s</td>\n", $i);
                        }
                        if($j==2)
                        {
                                printf("<td class=\"total2head\">Month</td>\n");
                        }

                        printf("</tr><tr><td class=\"datahead\">In</td>\n");

                        for($i=$start; $i<=$end; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable($days[$i]['in']));
                                $summonth['in'] += $days[$i]['in'];
                        }
                        if($j==2)
                        {
                                printf("<td class=\"total2\">%s</td>\n", humanreadable($summonth['in']));
                        }

                        printf("</tr><tr><td class=\"datahead\">Out</td>\n");

                        for($i=$start; $i<=$end; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable($days[$i]['out']));
                                $summonth['out'] += $days[$i]['out'];
                        }
                        if($j==2)
                        {
                                printf("<td class=\"total2\">%s</td>\n", humanreadable($summonth['out']));
                        }

                        printf("</tr><tr><td class=\"datahead\">Max</td>\n");

                        for($i=$start; $i<=$end; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable(max($days[$i]['in'], $days[$i]['out'])));
                        }
                        if($j==2)
                        {
                                printf("<td class=\"total2\">%s</td>\n", humanreadable(max($summonth['in'], $summonth['out'])));
                        }

                        printf("</tr><tr><td class=\"datahead\">Sum</td>\n");

                        for($i=$start; $i<=$end; $i++)
                        {
                                printf("<td class=\"data\">%s</td>\n", humanreadable($days[$i]['in'] + $days[$i]['out']));
                        }
                        if($j==2)
                        {
                                printf("<td class=\"total2\">%s</td>\n", humanreadable($summonth['in'] + $summonth['out']));
                        }

                        printf("</tr></table>\n");
                       
                } //for($j=1; $j<=2; $j++)


        } //showmonth($year, $month, $days)


/***************************************************************************\
*                                All the rest                               *
\***************************************************************************/


        /* Find legalnames */
        $legalnames = array();
        if($dirhandler = @opendir($dir))
        {
                while(($filename = readdir($dirhandler)) !== false)
                {
                        if(ereg("$extension$", $filename))
                        {
                                $filename = substr($filename, 0, -strlen($extension));
                                if(!in_array($filename, $exclude))
                                {
                                        $legalnames[] = $filename;
                                }
                        }
                }
                closedir($dirhandler);
        }



        /* If a device have been chosen */
        if(isset($_GET['name']))
        {

                /* If the device name is valid */
                if(validname($_GET['name']))
                {

                        /* If the script should generate a picture */
                        if(isset($_GET['picture']))
                        {

                                $name = filename($_GET['name']);

                                header("content-type: image/png");

                                $rrdcommand = "$rrdcommand graph - -v 'Bytes/s' -b 1024 -w 390 DEF:avgin=$name:ds0:AVERAGE AREA:avgin#00CC00:'Traffic in' DEF:avgout=$name:ds1:AVERAGE LINE2:avgout#0000FF:'Traffic out'";

                                /* Last day */
                                if($_GET['period']=='day')
                                {
                                        $rrdcommand .= ' -t "Traffic the last day" -s -86400';
                                }
                                /* Last week */
                                else if($_GET['period']=='week')
                                {
                                        $rrdcommand .= ' -t "Traffic the last week" -s -604800';
                                }
                                /* Last month */
                                else if($_GET['period']=='month')
                                {
                                        $rrdcommand .= ' -t "Traffic the last month" -s -2678400';
                                }
                                /* Last year */
                                else if($_GET['period']=='year')
                                {
                                        $rrdcommand .= ' -t "Traffic the last year" -s -31622400';
                                }
                                /* If year and month is supplied, then generate picture for that month */
                                else if(is_numeric($_GET['year']) && is_numeric($_GET['month']))
                                {
                                        $name = monthname($_GET['month']) . ' ' . $_GET['year'];
                                        $rrdcommand .= " -t 'Traffic for $name' " . monthstartend($_GET['year'], $_GET['month']);
                                        $rrdcommand .= " -x DAY:1:WEEK:1:DAY:1:86400:%d ";
                                }

                                echo  `$rrdcommand`;

                        } //if(isset($_GET['picture']))
                        /* If year and month is supplied, then generate page for that month */
                        else if(is_numeric($_GET['year']) && is_numeric($_GET['month']))
                        {

                                echo top($_GET['name']);

                                $name = monthname($_GET['month']) . ' ' . $_GET['year'];

                                printf("<img src=\"%s?name=%s&amp;year=%s&amp;month=%s&amp;picture=yes\" alt=\"%s\">", $_SERVER['SCRIPT_NAME'], $_GET['name'], $_GET['year'], $_GET['month'], $name);


                                $lastdate = 0;
                                $days = array();

                                /* Get statistics for the selected month */
                                if($fp = popen("$rrdcommand fetch " . filename($_GET['name']) . " AVERAGE -r 864000 ".monthstartend($_GET['year'], $_GET['month']), 'r'))
                                {
                                        fgets($fp, 4096);
                                        while(!feof($fp))
                                        {
                                                $line = trim(fgets($fp, 4096));

                                                if($line != '')
                                                {

                                                        list($date, $in, $out) = split('( )+', $line);
                                                        list($date) = split(':', $date);
                                                        if($lastdate != 0)
                                                        {

                                                                if(!is_numeric($in))
                                                                        $in = 0;
                                                                if(!is_numeric($out))
                                                                        $out = 0;

                                                                $in  = $in*($date-$lastdate);
                                                                $out = $out*($date-$lastdate);

                                                                if($_GET['month'] == date('n', $lastdate) && $_GET['year'] == date('Y', $lastdate))
                                                                {
                                                                        $day = date('j', $lastdate);
                                                                        $days[$day]['in']  += $in;
                                                                        $days[$day]['out'] += $out;
                                                                }

                                                        } //if($lastdate != 0)

                                                        $lastdate = $date;

                                                } //if($line != '')
                                        } //while(!feof($fp))

                                        showmonth($_GET['year'], $_GET['month'], $days);

                                        pclose($fp);

                                } //if($fp = popen($test, 'r'))


                                echo bottom();

                        }
                        /* Else generate main device page */
                        else
                        {

                                echo top($_GET['name']);

                                /* Find out when the database was last updated */
                                if($fp = popen("$rrdcommand info " . filename($_GET['name']), 'r'))
                                {
                                        $key = '';
                                        while(!feof($fp))
                                        {
                                                list($key, $value) = split(' = ', trim(fgets($fp, 4096)));
                                                if($key == 'last_update')
                                                {
                                                        printf("Last updated: %s<br>\n", date("Y-m-d H:i:s", $value));
                                                        break;
                                                }
                                        }
                                        pclose($fp);
                                }

                                printf("<img src=\"%s?name=%s&period=day&picture=yes\" alt=\"Dayly\">", $_SERVER['SCRIPT_NAME'], $_GET['name']);
                                printf("<img src=\"%s?name=%s&period=week&picture=yes\" alt=\"Weekly\">", $_SERVER['SCRIPT_NAME'], $_GET['name']);
                                printf("<img src=\"%s?name=%s&period=month&picture=yes\" alt=\"Monthly\">", $_SERVER['SCRIPT_NAME'], $_GET['name']);
                                printf("<img src=\"%s?name=%s&period=year&picture=yes\" alt=\"Yearly\">\n", $_SERVER['SCRIPT_NAME'], $_GET['name']);


                                $lastdate = 0;
                                $months = array();

                                /* Get statistics for the last two year */
                                if($fp = popen("$rrdcommand fetch " . filename($_GET['name']) . " AVERAGE -s -63331200 -e +31622400", 'r'))
                                {
                                        fgets($fp, 4096);
                                        while(!feof($fp))
                                        {
                                                $line = trim(fgets($fp, 4096));
                                                if($line != '')
                                                {

                                                        list($date, $in, $out) = split('( )+', $line);
                                                        list($date) = split(':', $date);
                                                        if($lastdate != 0)
                                                        {

                                                                if(!is_numeric($in))
                                                                        $in = 0;
                                                                if(!is_numeric($out))
                                                                        $out = 0;

                                                                $in  = $in*($date-$lastdate);
                                                                $out = $out*($date-$lastdate);

                                                                $year = date('Y', $lastdate);
                                                                $month = date('n', $lastdate);
                                                                $months[$year][$month]['in']  += $in;
                                                                $months[$year][$month]['out'] += $out;

                                                        } //if($lastdate != 0)

                                                        $lastdate = $date;

                                                } //if($line != '')
                                        } //while(!feof($fp))

                                        $year = date('Y');
                                        showyear($year, $months[$year]);

                                        $year = date('Y')-1;
                                        showyear($year, $months[$year]);

                                        pclose($fp);

                                } //if($fp = popen($test, 'r'))


                                echo bottom();

                        } //else


                } //if(validname($_GET['name']))
                /* If device name has been provided, but it is not valid */
                else
                {
                        printf("Don't do that");
                } //else


        } //if(isset($_GET['name']))
        /* If device name has been given, show the main page */
        else
        {

                echo top('All devices');

                foreach($legalnames as $name)
                {
                        printf("<a href=\"%s?name=%s\">%s</a><br>\n", $_SERVER['SCRIPT_NAME'], $name, $name);
                }

                echo bottom();

        } //else


?>


Добавляем весь этот код в файл index.php.
Заходим на свой вебсервер в необходимый каталог и наслаждаемся статистикой.