Сага о биллинге, или Считаем трафик на FreeBSD (ng_ipacct + Perl + MySQL). Часть 2::Журнал СА 3.2005
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Подписка
Архив номеров
Где купить
Наука и технологии
Авторам
Рекламодателям
Контакты
   

  Опросы
  Статьи

Дата-центры  

Дата-центры: есть ли опасность утечки данных?

Российские компании уже несколько лет испытывают дефицит вычислительных мощностей. Рост числа проектов,

 Читать далее...

Событие  

В банке рассола ждет сисадмина с полей фрактал-кукумбер

Читайте впечатления о слете ДСА 2024, рассказанные волонтером и участником слета

 Читать далее...

Организация бесперебойной работы  

Бесперебойная работа ИТ-инфраструктуры в режиме 24/7 Как обеспечить ее в нынешних условиях?

Год назад ИТ-компания «Крок» провела исследование «Ключевые тренды сервисного рынка 2023». Результаты

 Читать далее...

Книжная полка  

Читайте и познавайте мир технологий!

Издательство «БХВ» продолжает радовать выпуском интересных и полезных, к тому же прекрасно

 Читать далее...

СУБД PostgreSQL  

СУБД Postgres Pro

Сертификация по новым требованиям ФСТЭК и роль администратора без доступа к данным

 Читать далее...

Критическая инфраструктура  

КИИ для оператора связи. Готовы ли компании к повышению уровня кибербезопасности?

Похоже, что провайдеры и операторы связи начали забывать о требованиях законодательства

 Читать далее...

Архитектура ПО  

Архитектурные метрики. Качество архитектуры и способность системы к эволюционированию

Обычно соответствие программного продукта требованиям мы проверяем через скоуп вполне себе понятных

 Читать далее...

Как хорошо вы это знаете  

Что вам известно о разработках компании ARinteg?

Компания ARinteg (ООО «АРинтег») – системный интегратор на российском рынке ИБ –

 Читать далее...

Графические редакторы  

Рисование абстрактных гор в стиле Paper Cut

Векторный графический редактор Inkscape – яркий представитель той прослойки open source, с

 Читать далее...

День сисадмина  

Учите матчасть! Или как стать системным администратором

Лето – время не только отпусков, но и хорошая возможность определиться с профессией

 Читать далее...

День сисадмина  

Живой айтишник – это всегда движение. Остановка смерти подобна

Наши авторы рассказывают о своем опыте и дают советы начинающим системным администраторам.

 Читать далее...

Виртуализация  

Рынок решений для виртуализации

По данным «Обзора российского рынка инфраструктурного ПО и перспектив его развития», сделанного

 Читать далее...

Книжная полка  

Как стать креативным и востребованным

Издательский дом «Питер» предлагает новинки компьютерной литературы, а также книги по бизнесу

 Читать далее...

Книжная полка  

От создания сайтов до разработки и реализации API

В издательстве «БХВ» недавно вышли книги, которые будут интересны системным администраторам, создателям

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 6241
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 6947
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

12.03.2018г.
Просмотров: 4233
Комментарии: 0
Глубокое обучение с точки зрения практика

 Читать далее...

12.03.2018г.
Просмотров: 3015
Комментарии: 0
Изучаем pandas

 Читать далее...

12.03.2018г.
Просмотров: 3813
Комментарии: 0
Программирование на языке Rust (Цветное издание)

 Читать далее...

19.12.2017г.
Просмотров: 3828
Комментарии: 0
Глубокое обучение

 Читать далее...

19.12.2017г.
Просмотров: 6323
Комментарии: 0
Анализ социальных медиа на Python

 Читать далее...

19.12.2017г.
Просмотров: 3174
Комментарии: 0
Основы блокчейна

 Читать далее...

19.12.2017г.
Просмотров: 3467
Комментарии: 0
Java 9. Полный обзор нововведений

 Читать далее...

16.02.2017г.
Просмотров: 7285
Комментарии: 0
Опоздавших не бывает, или книга о стеке

 Читать далее...

17.05.2016г.
Просмотров: 10652
Комментарии: 0
Теория вычислений для программистов

 Читать далее...

30.03.2015г.
Просмотров: 12370
Комментарии: 0
От математики к обобщенному программированию

 Читать далее...

18.02.2014г.
Просмотров: 14008
Комментарии: 0
Рецензия на книгу «Читаем Тьюринга»

 Читать далее...

13.02.2014г.
Просмотров: 9132
Комментарии: 0
Читайте, размышляйте, действуйте

 Читать далее...

12.02.2014г.
Просмотров: 7084
Комментарии: 0
Рисуем наши мысли

 Читать далее...

10.02.2014г.
Просмотров: 5394
Комментарии: 3
Страна в цифрах

 Читать далее...

18.12.2013г.
Просмотров: 4623
Комментарии: 0
Большие данные меняют нашу жизнь

 Читать далее...

18.12.2013г.
Просмотров: 3433
Комментарии: 0
Компьютерные технологии – корень зла для точки роста

 Читать далее...

04.12.2013г.
Просмотров: 3165
Комментарии: 0
Паутина в облаках

 Читать далее...

03.12.2013г.
Просмотров: 3406
Комментарии: 0
Рецензия на книгу «MongoDB в действии»

 Читать далее...

02.12.2013г.
Просмотров: 3031
Комментарии: 0
Не думай о минутах свысока

 Читать далее...

Друзья сайта  

 Сага о биллинге, или Считаем трафик на FreeBSD (ng_ipacct + Perl + MySQL). Часть 2

Архив номеров / 2005 / Выпуск №3 (28) / Сага о биллинге, или Считаем трафик на FreeBSD (ng_ipacct + Perl + MySQL). Часть 2

Рубрика: Администрирование /  Продукты и решения

ВЛАДИМИР ЧИЖИКОВ

Сага о биллинге, или Считаем трафик на FreeBSD (ng_ipacct + Perl + MySQL)

Часть 2

В предыдущей части статьи мы рассмотрели, как установить и запустить ng_ipacct, а также рассмотрели создание своих собственных скриптов для запуска и остановки разрабатываемой системы учета трафика.

Дальнейшая цель – получить статистику и поместить ее в базу. Что нам для этого нужно? В первой части статьи, когда описывался ng_ipacct, указывалось, что для снятия статистики необходимо последовательно проделать следующее: передать данные в checkpoint-базу, потом вывести данные при помощи show (перенаправить в файл) и очистить checkpoint для получения следующей порции данных.

Таким образом, мы сразу же определили, что нам нужно сложить статистику в файл при помощи перенаправления вывода show. А после этого, уже считывая из файла данные, отправить в базу. Для того чтобы не было смешивания всех интерфейсов в одном файле, мы также должны условиться заранее, что для каждого интерфейса будет создан свой собственный файл статистики, а также один общий, куда будет складываться статистика со всех интерфейсов. В этих файлах будет указано имя хоста, время получения порции записей, дата и самое главное – интерфейс. Почему так акцентируется внимание на интерфейсе? Очень просто. У нас могут быть каналы на одной машине, где локальный трафик считается, а также где он бесплатный. Учесть нам необходимо платный. Соответственно нужно знать, какой интерфейс принял или отправил пакет.

Что ж, основная установка сделана. Остальное – по ходу повествования.

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

mysql> create database ng_stat;

Query OK, 1 row affected (0.04 sec)

mysql> grant insert,create,update,select,delete on ng_stat.* to nguser@"%" identifiedby "ngpassword";

Query OK, 0 rows affected (0.08 sec)

Одновременно были даны права пользователю nguser на добавление, обновление, удаление записей и их выборку, а также на создание таблиц.

Итак, вновь возвращаемся к написанию скриптов:

# touch ng_stat_in.pl

И начинаем вносить данные. Первым делом необходимо подключить два модуля perl, которые будут использоваться:

#!/usr/bin/perl -w

use DBI;

use Time::localtime;

Последний у вас должен быть по умолчанию в системе, а вот наличие DBI необходимо проверить. Самый простой способ – отправить на исполнение скрипт уже в таком виде. Выдаст ошибку – значит, отсутствует или не соответствует текущей версии perl (например, вы обновили perl, а все сопутствующие модули нет).

Что ж, это поправимо:

# cd /usr/ports/databases/p5-DBI/

# make && make install && make clean && rehash

# cd /usr/ports/databases/p5-DBD-mysql

# make && make install && make clean && rehash

Если у вас стоит MySQL не 3.23 версии, а 4 и выше, то выберите соответствующий вариант вместо p5-DBD-mysql. После этого можно смело приступать к дальнейшим манипуляциям.

Для подключения к базе данных нужно снова считать конфигурационный файл и все параметры, необходимые для того, чтобы выяснить:

  • какие интерфейсы подключены;
  • имя сервера базы данных;
  • имя базы данных;
  • имя и пароль пользователя для доступа к базе.

Все это описано в конфигурационном файле (смотрите первую часть статьи в №2, 2005 г.). Но сначала опять нужно задать основные переменные.

# Список основных переменных

my $serverdb = "test";

my $dbname = "test";

my $dbuser = "test";

my $dbpass = "test";

my $table_auth = "test";

my $table_proto = "test";

my $listen_host = "test";

my @listen_interf;

my @ng_modules;

my $ng_modules_def = "netgraph,ng_ether,ng_socket,ng_tee,ng_ipacct";

my $threshold = 5000;

my $ipacct_log = '/usr/local/script/ng_stat/log/ng.log';

Некоторые из них нам не потребуются. Но это удобная заготовка для всех скриптов. Мы по очереди вносим необходимые параметры, всего лишь модернизируя уже имеющийся скрипт. «Лишние» переменные можно будет убрать на этапе отладки.

Самое важное в этом списке «my $ipacct_log = “/usr/local/script/ng_stat/log/ng.log”» – мы указали расположение основного файла, куда по умолчанию будет записываться вся статистика (с интерфейсами, временем и т. д.).

Что ж, читаем дальше конфигурационный файл. Он остается без изменений, так что приводить его не буду.

Проверяем время на машине. Именно это время и будет записываться в базу:

# Проверяем время.

$gm = localtime();

$year = ($gm->year()) + 1900;

$mounth = ($gm->mon()) + 1;

$mday = $gm->mday();

$date = "$mday-$mounth-$year";

$hour = $gm->hour();

$min = $gm->min();

$sec = $gm->sec();

$hour=sprintf("%02d",$hour);

$min=sprintf("%02d",$min);

$sec=sprintf("%02d",$sec);

$time = "$hour\:$min\:$sec";

$table_date = "$year\_$mounth";

Почему в переменной $year мы добавляем 1900? Очень просто – она ведет отсчет от 1900 года. Почему в месяцах прибавляем единицу? Переменная возвращает значения от 0 до 11.

Функция sprintf вернет значения переменных $hour, $sec и $min числом из двух цифр, если полученное значение будет меньше 10. Например, одна секунда, после получения ее значения, будет 1, а нужно 01.

Последний параметр $table_date определяет имя таблицы в базе данных.

Далее идет конструкция для проверки, установлены интерфейсы или нет. Если все в порядке, начинаем подготовку к тому, чтобы закачивать данные на сервер.

Первым делом необходимо получить данные с интерфейсов и записать во временные файлы.

while (@listen_interf){

           $interface = shift @listen_interf;

           my $pid;

           $pid = fork;

           if (defined $pid) {

                 if ($pid == 0){

#$IPACCTCTL ${IFACE}_ip_acct:$IFACE checkpoint

                        exec "/usr/local/sbin/ipacctctl $interface\_ip_acct:$interface checkpoint" \

                               or die "Ошибка передачи записи в checkpoint-базу!\n";

                        exit;

                 }

           }

           else {

                 print "Фатальная ошибка ветвления!\n.................\n";

                 die "Разделение на процессы невозможно.\n Принудительный выход из дочернего процесса: $!\n";

           }

           do {

                 $kid = waitpid $pid,0;

                 if ($kid == -1) {

                 print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";

                 } elsif ($kid == 0) {

                        print "Задан не блокирующий вызов и процесс еще не завершен!\n";

                 }

           } until $kid=$pid;

           undef $pid;

           $pid = fork;

           if (defined $pid) {

                 if ($pid == 0){

#$IPACCTCTL ${IFACE}_ip_acct:$IFACE show >> $DIR/$SDIR/$NAME

                 exec "/usr/local/sbin/ipacctctl $interface\_ip_acct:$interface show >> $ipacct_log\.$interface" or die \

                        "Ошибка передачи записей из checkpoint-базы в файл!\n";

                        exit;

                 }

           }

           else {

                 print "Фатальная ошибка ветвления!\n.................\n";

                 die "Разделение на процессы невозможно.\n Принудительный выход из дочернего процесса: $!\n";

           }

           do {

                 $kid = waitpid $pid,0;

                 if ($kid == -1) {

                 print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";

                 } elsif ($kid == 0) {

                        print "Задан не блокирующий вызов и процесс еще не завершен!\n";

                 }

           } until $kid=$pid;

           undef $pid;

           $pid = fork;

           if (defined $pid) {

                 if ($pid == 0){

#$IPACCTCTL ${IFACE}_ip_acct:$IFACE clear

                 exec "/usr/local/sbin/ipacctctl $interface\_ip_acct:$interface clear" or die \

                        "Ошибка при очистке checkpoint-базы! \nБаза не очищена. Возможно переполнение. Очистите базу в ручную\n";

                        exit;

                 }

           }

           else {

                 print "Фатальная ошибка ветвления!\n.................\n";

                 die "Разделение на процессы невозможно.\n Принудительный выход из дочернего процесса: $!\n";

           }

           do {

                 $kid = waitpid $pid,0;

                 if ($kid == -1) {

                 print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";

                 } elsif ($kid == 0) {

                        print "Задан не блокирующий вызов и процесс еще не завершен!\n";

                 }

           } until $kid=$pid;        

           undef $pid;              

           $TMPLOG= "$ipacct_log\.$interface";

           open (TMPLOG, "$TMPLOG");

           $TMPLOG =~ s/\||`|&&|<|>//gi; #Очистка ряда символов | ` && < > из пути к файлу.

           while (<TMPLOG>){

                 $tmp_log_line=$_;         

                 chomp $tmp_log_line;

                 $tmp_log_line = "$tmp_log_line $date $time $listen_host $interface";

                 push @ipacct_arr,$tmp_log_line;

           }

           close (TMPLOG);

           truncate ($TMPLOG,0);         

           undef $pid;

    }

Обращаю внимание на то, что полный путь к ipacctctl хранится в переменной $ipacctctl – так как скрипт будет работать по cron, то здесь желательно указать полный путь к нему, ибо не всегда cron сможет получить переменные из профиля того пользователя, от имени которого будет исполняться команда или программа.

Как видите, первыми идут checkpoint, show, clear. На этапе show мы перенаправляем данные во временный файл. Временный файл определяется основным файлом статистики с приставкой имени интерфейса, то есть для rl0 он будет выглядеть как /usr/local/script/ng_stat/log/ng.log.rl0. И так поочередно для каждого из интерфейсов. После занесения данных эти файлы считываются. Каждая строка из них будет дополнена необходимой информацией (дата, время, имя хоста, интерфейс) и занесена в массив.

$tmp_log_line = "$tmp_log_line $date $time $listen_host $interface";

push @ipacct_arr,$tmp_log_line;

После того, как временный файл считан до конца, мы его очищаем (можно в принципе и удалить, хотя это неэффективно).

truncate(“$TMPLOG”,0);

так поочередно мы заполним данными со всех интерфейсов массив @ipacct_arr. Его, кстати, необходимо внести в список основных переменных, которые были объявлены в начале скрипта.

my @ipacct_arr;

my @ipacct_arr_in;

Я указал кроме него еще один массив – он сейчас тоже потребуется.

open (IPCTLOG,">>$ipacct_log");

while (@ipacct_arr){

    $line_arr = shift @ipacct_arr;

    $line_arr = "$line_arr\n";

    print IPCTLOG $line_arr;

}

close(IPCTLOG);

Этим действием все содержимое массива, полученного на предыдущем шаге, заносится в основной файл статистики. Теперь в случае любых перипетий (недоступность сервера, отсутствие созданной базы или неправильный логин/пароль) вся статистика будет накапливаться в нем. Именно поэтому и было указано то, что в случае пополнения записей они должны дописываться в конец файла.

open (IPCTLOG,">>$ipacct_log");

Статистика получена. Теперь наступил самый ответственный этап. Необходимо привести к нужному виду каждую строку в файле статистики. Проверить доступность сервера и необходимой базы и таблицы. Если все в полном порядке, то внести данные. Вот как полностью будет выглядеть этот блок:

     while (@listen_interf){

           $interface = shift @listen_interf;

           my $pid;

           $pid = fork;

           if (defined $pid) {

                 if ($pid == 0){

#$IPACCTCTL ${IFACE}_ip_acct:$IFACE checkpoint

                        exec "/usr/local/sbin/ipacctctl $interface\_ip_acct:$interface checkpoint" or die \

                               "Ошибка передачи записи в checkpoint-базу!\n";

                        exit;

                 }

           }

           else {

                 print "Фатальная ошибка ветвления!\n.................\n";

                 die "Разделение на процессы невозможно.\n Принудительный выход из дочернего процесса: $!\n";

           }

           do {

                 $kid = waitpid $pid,0;

                 if ($kid == -1) {

                 print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";

                 } elsif ($kid == 0) {

                        print "Задан не блокирующий вызов и процесс еще не завершен!\n";

                 }

           } until $kid=$pid;      

           undef $pid;      

           $pid = fork;

           if (defined $pid) {

                 if ($pid == 0){

#$IPACCTCTL ${IFACE}_ip_acct:$IFACE show >> $DIR/$SDIR/$NAME

                 exec "/usr/local/sbin/ipacctctl $interface\_ip_acct:$interface show >> $ipacct_log\.$interface" or die \

                        "Ошибка передачи записей из checkpoint-базы в файл!\n";

                        exit;

                 }

           }

           else {

                 print "Фатальная ошибка ветвления!\n.................\n";

                 die "Разделение на процессы невозможно.\n Принудительный выход из дочернего процесса: $!\n";

           }

           do {

                 $kid = waitpid $pid,0;

                 if ($kid == -1) {

                 print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";

                 } elsif ($kid == 0) {

                        print "Задан не блокирующий вызов и процесс еще не завершен!\n";

                 }

           } until $kid=$pid;        

           undef $pid;       

           $pid = fork;

           if (defined $pid) {

                 if ($pid == 0){

#$IPACCTCTL ${IFACE}_ip_acct:$IFACE clear

                 exec "/usr/local/sbin/ipacctctl $interface\_ip_acct:$interface clear" or die \

                        "Ошибка при очистке checkpoint-базы! \nБаза не очищена. Возможно переполнение. Очистите базу вручную\n";

                        exit;

                 }

           }

           else {

                 print "Фатальная ошибка ветвления!\n.................\n";

                 die "Разделение на процессы невозможно.\n Принудительный выход из дочернего процесса: $!\n";

           }

           do {

                 $kid = waitpid $pid,0;

                 if ($kid == -1) {

                 print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";

                 } elsif ($kid == 0) {

                        print "Задан не блокирующий вызов и процесс еще не завершен!\n";

                 }

           } until $kid=$pid;        

           undef $pid;             

           $TMPLOG= "$ipacct_log\.$interface";

           open (TMPLOG, "$TMPLOG");

           $TMPLOG =~ s/\||`|&&|<|>//gi; #Очистка ряда символов | ` && < > из пути к файлу.

           while (<TMPLOG>){

                 $tmp_log_line=$_;         

                 chomp $tmp_log_line;

                 $tmp_log_line = "$tmp_log_line $date $time $listen_host $interface";

                 push @ipacct_arr,$tmp_log_line;

           }

           close (TMPLOG);

           truncate ($TMPLOG,0);               

           undef $pid;

    }  

    open (IPCTLOG,">>$ipacct_log");

    while (@ipacct_arr){

           $line_arr = shift @ipacct_arr;

           $line_arr = "$line_arr\n";

           print IPCTLOG $line_arr;   

    }

    close(IPCTLOG);    

    &parse_log_file;

    &check_in_mysql;

    &insert_data_db;

}

Как видно из кода, присутствует вызов трех подпрограмм. Выполняемые ими функции интуитивно понятны из названия (&parse_log_file; &check_in_mysql; &insert_data_db;).

Рассмотрим их поочередно.

sub parse_log_file {

open (PARSFILE, "$ipacct_log");

while ($line_parse=<PARSFILE>) {

        chomp $line_parse;

        $line_parse =~ s/[\s\t]+/\t/g;

        push @ipacct_arr_in, $line_parse;

}

close (PARSFILE);

truncate ("$ipacct_log",0);

}

Все, что мы делаем здесь, – производим разбор строки основного файла. И все имеющиеся символы пробела или табуляции заменяем на единичные символы табуляции. И вносим данные в объявленный выше массив @ipacct_arr_in. После того как все данные из файла были внесены в массив, этот файл обнуляется для записи последующей порции данных.

Что ж, проверим доступность mysql и наличия таблиц:

my ($dbh,$sth,$count);

$dbh = DBI->connect("DBI:mysql:host=$serverdb;database=$dbname", "$dbuser", "$dbpass")

                or &error_connection;

$sth = $dbh->prepare("SHOW tables");

$sth->execute ();

Первой строкой мы объявили переменные, которые будут использоваться для соединения. Второй устанавливаем соединение с MySQL. В ней указываем, что необходимо использовать драйвер mysql DBI, также расположение сервера, БД, имя и пароль, которые получили из файла настройки. В случае, если произойдут ошибки, будет выполнена подпрограмма &error_connection. Ее опишем несколько позже, а пока условимся, что соединение прошло успешно. Следующим пунктом будет запрос. В данном случае проверяется наличие необходимых таблиц в базе (SHOW TABLES), а последняя строка означает выполнение запроса.

Теперь полученный результат занесем в массив:

my @row;

my $tables;

while (@row = $sth->fetchrow_array) {

    foreach $tables (@row){

           push @dbtables, $tables;

    }

}

Самое интересное во всем этом – оператор foreach, который присваивает переменной $table значения массива @row. Значения этой переменной заносятся в @tables.

$crt_tbl="yes";

while (@dbtables) {

    $table = shift @dbtables;

    if (defined $table) {

           if ($table eq $table_date) {

                 $crt_tbl="no";

           }

    }

}

В данном блоке устанавливается значение переменной $crt_tbl в yes, чтобы в случае необходимости создать таблицу, определенную в переменной $table_date. Последующие действия как раз и описывают этап сравнения элементов массива с переменной. Если таблица с таким именем присутствует, то $crt_tbl принимает значение no.

if ($crt_tbl eq "yes") {

#   print "Создаем таблицу\n";

    &crt_table_log;

}

$sth->finish;

$dbh->disconnect;

Если такой таблицы нет, она будет создана при вызове подпрограммы &crt_table_log.

В этом модуле встречаются две новые подпрограммы. Опишем первую, так как она используется еще в нескольких местах. Итак, в случае ошибки соединения необходимо срочно остановить выполнение скрипта и сбросить данные обратно в файл.

sub error_connection {

print "Проверьте правильность имени и пароля на базу в MySQL, ее существование\n";

print "Возможной причиной ошибки также может являться то, что сервер временно недоступен\n";

print "Будет произведено копирование всех данных в файл:\n\n$ipacct_log \n\n";

print "Накопление статистики в файл не лимитировано, но это может повлечь за собой";

print " всплеск нагрузки на сеть и сервера. Поэтому обратите внимание на данное";

print " сообщение и выясните конкретную причину.\n";

foreach $line_arr(@ipacct_arr_in) {

    open (DUMPFILE, ">>$ipacct_log");

    $line_arr = "$line_arr\n";

    print DUMPFILE $line_arr;

    close (DUMPFILE);

}

die "Выход.\n";

}

Вторая создает таблицу, в которую будет производиться запись данных.

sub crt_table_log {

    my ($dbh,$sth,$count);

    $dbh = DBI->connect("DBI:mysql:host=$serverdb; database=$dbname", "$dbuser", "$dbpass")

           or &error_connection;

    $select = "CREATE  TABLE $table_date (ip_from varchar(255),s_port varchar(128),ip_to varchar(255), d_port   varchar(128), \

           proto varchar(32), packets int(8), bytes int(16) default 0,date_ins varchar(32), time_ins time, host varchar(128), \

           interface varchar(8), index (ip_from),index (ip_to),index (proto), index (packets), index (bytes),index (host), \

           index (time_ins), index (date_ins), index (interface))";

    $sth = $dbh->prepare("$select");

    $sth->execute ();

    $sth->finish;

    $dbh->disconnect;

}

Ну и наконец последнее – заносим данные в базу:

sub insert_data_db {

my ($dbh,$sth,$count);

$dbh = DBI->connect("DBI:mysql:host=$serverdb; database=$dbname","$dbuser","$dbpass")

                or &error_connection_in;

$insert = "INSERT INTO $table_date (ip_from,s_port,ip_to,d_port,proto,packets,bytes,date_ins,time_ins,host,interface) \

    VALUES (?,?,?,?,?,?,?,?,?,?,?)";

$sth = $dbh->prepare("$insert");

print "$insert\n";

while (@ipacct_arr_in) {

    $line_in = shift @ipacct_arr_in;  ($ip_from, $s_port,$ip_to,$d_port,$proto,$packets,$bytes,$date_ins,\

           $time_ins,$host,$interface)=split(/[\s\t]+/,$line_in);

    if (!defined $proto){

           $proto="0";

    }

    if (!defined $packets){

           $packets="0";

    }

    if (!defined $bytes){

           $bytes="0";

    }

    $sth->execute ($ip_from,$s_port,$ip_to,$d_port,$proto,$packets,$bytes,$date_ins,$time_ins,$host,$interface);

}

$sth->finish;

$dbh->disconnect;

}

Как видите, снова идет пошаговое считывание данных из массива. Полученные строки разбиваются на составляющие при помощи split. Если значения в этих переменных отсутствуют, им присваивается значение, равное нулю.

Вот в принципе все. Мы занесли данные в таблицу. Теперь можно извлекать нужные данные соответствующими запросами на выборку. Полностью содержимое можно посмотреть в ng_stat_in.pl. Последние штрихи – помещение созданного сценария в /usr/local/etc/rc.d и добавление подобной записи в /etc/crontab.

*/15       *      *      *      *      root   /usr/local/script/ng_stat/bin/ng_stat_in.pl

Наша система готова к сбору статистики. Конечно, она не лишена ряда недостатков. Таковыми можно считать то, что система работает именно от пользователя root, и не имеет возможности на лету менять конфигурацию. Самое главное ее преимущество – простота. Она предоставляет самое удобное хранилище данных, откуда их можно вытянуть и при помощи web, и с консоли, и даже с машины под управлением Windows.

Итак, были выполнены практически все требования к биллингу. Мы остановимся пока на этом, ибо немаленький объем вышел для трех простеньких скриптов. Если у читателей появится желание, приведу дополнительный набор скриптов и их описание, систему авторизации через веб-интерфейс и, если необходимо, графические клиенты под X-Window и MS Windows.

В принципе вы и сами можете написать самые простые запросы уже сейчас. Приведу еще раз заголовки таблицы:

mysql> show columns from 2004_10;

Field            Type            Null        Key      Def      Ext

ip_from            varchar(255)      YES         MUL      NULL            

s_port            varchar(128)      YES                NULL            

ip_to            varchar(255)      YES         MUL      NULL            

d_port            varchar(128)      YES                NULL            

proto            varchar(32)      YES         MUL      NULL            

packets            int(8)            YES         MUL      NULL            

bytes            int(16)            YES         MUL       0

date_ins            varchar(32)      YES         MUL      NULL            

time_ins            time            YES         MUL      NULL            

host            varchar(128)      YES         MUL      NULL            

interface            varchar(8)      YES         MUL      NULL            

11 rows in set (0.02 sec)

И простенький запрос к базе на выборку за 10 месяц 2004 года суммы прошедшего трафика через интерфейс rl0 сервера freebsd2:

mysql> select sum(bytes) from 2004_10 where host="freebsd2" and interface="rl0";

sum(bytes)

993453162

1 row in set (0.12 sec)

Все остальные скрипты, вне зависимости от своего назначения, являются вариациями на тему запроса.

Подведем итоги

Разобранные выше примеры написания системы учета трафика – не полноценный биллинг, так как для такой системы нужно хорошо просчитать структуру самой БД, ее нагрузку, выбрать оптимальные типы полей в таблицах. Для примера, в более серьезном и нагруженном варианте (сервер провайдера и порядка 20 хостов) необходимо изменить типы полей s_port, d_port, ip_from, ip_to на тип int (преобразование IP-адреса выполняется встроенными функциями MySQL), одним словом, уделить очень большое внимание настройке оптимальной производительности самой СУБД – здесь она станет узким местом, и, возможно, перейти на альтернативную СУБД.

Но подводя итоги этой статьи, можно с уверенностью сказать самое главное: ng_ipacct является очень удобным программным пакетом для сбора полной и детализированной статистики. Причем он не требует каких-то особых заоблачных знаний и предоставляет удобную в отображении информацию. Он может заменить вам систему учета трафика, даже если вы будете каждый день мигрировать с одного типа брандмауэра на другой. При этом особо не нагрузит ваш сервер.

Также можно добавить, что сам процесс сбора статистики и запись полученных данных средствами perl не представляет особой сложности – весь необходимый набор инструментов встроен в perl либо присутствует в виде портов и пакетов. Большую часть подпрограмм (например, чтение конфигурационного файла) можно вынести в отдельный модуль/пакет. Также вполне возможно, что вы несколько перепишете код под себя – тут уж никто никого не сдерживает. Цель есть, а как к ней добраться – каждый выбирает сам.


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-45
E-mail: sa@samag.ru