Advertisement
Guest User

script for check synchronization of zabbix 2.0 nodes

a guest
Aug 25th, 2013
384
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 13.41 KB | None | 0 0
  1. #!/usr/bin/perl
  2.  
  3. # 2013.08.25, v2.1, Oleg Korchagin
  4. # http://www.linux.org.ru/forum/admin/9306990
  5. # скипт, проверяющий синхронизацию нод zabbix
  6. # шаблон здесь: http://pastebin.com/JKEpkLt6
  7.  
  8. # установка:
  9. # 1) подключить клиент с slave node НЕПОСРЕДСТВЕННО к master node
  10. # 2) импортировать шаблон, назначить его на ноду из п.1
  11. # 3) установить данный скрипт на master node. crontab:
  12. #        # раз в 5 минут собирать данные из БД master node. Обратите внимание на ключ --current_node master
  13. #        */5 * * * * /usr/local/bin/check_zabbix_node_sync.pl --dbhost 10.x.x.x --current_node master --check_node_id 205 --check_node_name vml-slave-zabbix --zabbix_server 10.y.y.y  --mode collect >> /var/log/zabbix/check_zabbix_node_205_collect.log 2>&1
  14. #        # раз в час выполнять low level discovery. Да, число таклиц фиксированное и можно было бы забить в шаблон, но так удобнее.
  15. #        5 * * * * /usr/local/bin/check_zabbix_node_sync.pl --dbhost 10.x.x.x --current_node master --check_node_id 205 --check_node_name vml-slave-zabbix --zabbix_server 10.y.y.y  --mode discover >> /var/log/zabbix/check_zabbix_node_205_discover.log 2>&1
  16.  
  17. # , где
  18. # mode    - collect | discover
  19. # current_node - master | slave.
  20. # zabbix_server   - адрес _master_ node
  21. # check_node_id - node id для _slave_ node
  22. # check_node_name - имя _slave_ node в zabbix
  23. # dbhost, dbport, dbname, dbuser, dbpassword - параметры для подключения к БД zabbix
  24.  
  25. # 4) установить скрипт на slave node. crontab:
  26. #        */5 * * * * /usr/local/bin/check_zabbix_node_sync.pl --current_node slave --check_node_id 205 --check_node_name vml-slave-zabbix --zabbix_server 10.y.y.y  --mode collect >> /var/log/zabbix/check_zabbix_node_205_collect.log 2>&1
  27.  
  28.  
  29.  
  30. use strict;
  31. use DBI;
  32. use Data::Dumper;
  33. use Time::Local;
  34.  
  35. use Getopt::Std;
  36. use Getopt::Long;
  37.  
  38. my $version = '2.1';
  39. my $debug = 0; # печатать данные на экран вместо отправки в zabbix
  40. my $help=0;
  41.  
  42. ################################################################################
  43. # переменные. можно редактировать здесь или указывать в командной строке
  44. # см. ниже usage и GetOptions
  45. ################################################################################
  46.  
  47. # параметры подключения к БД.
  48. my $dbname='zabbix';
  49. my $dbuser='zabbix';
  50. my $dbpassword='password';
  51. my $dbhost;
  52. my $dbport=5432;
  53.  
  54. my $current_node; # slave | master
  55. my $check_node_id;  # slave node id
  56. my $zabbix_object; # имя slave node в zabbix
  57. my $mode = 'collect'; # discover|collect
  58.  
  59. my $zabbix_server; # адрес master node
  60.  
  61.  
  62.  
  63. ################################################################################
  64. # отправка данных в zabbix
  65. ################################################################################
  66. sub zabbix_directly_send($$){
  67.     my (  $key, $value ) = @_;
  68.     if ( $debug ) {
  69.         system( "echo zabbix_sender -vvvv -z $zabbix_server -s \"$zabbix_object\" -k \"$key\" -o '$value'" );
  70.     } else {
  71.         system( "zabbix_sender -vvvv -z $zabbix_server -s \"$zabbix_object\" -k \"$key\" -o '$value'" );
  72.     }
  73. }
  74.  
  75. my @zabbix_queue;
  76. sub zabbix_add_to_queue($$$) {
  77.     my ( $zabbix_object, $key, $value ) = @_;
  78.     push @zabbix_queue, sprintf "%s\t%s\t%s", $zabbix_object, $key, $value;
  79. }
  80.  
  81. sub zabbix_bulk_send() {
  82.     my $ZS;
  83.     if ( $debug  ) { # всё выводим на экран
  84.         print "zabbix_sender -vvvv -z \"$zabbix_server\" -s \"$zabbix_object\" -i -\n";
  85.         open $ZS, "| cat";
  86.     } else { # отправляем в zabbix
  87.         open $ZS, "| zabbix_sender -vvvv -z \"$zabbix_server\" -s \"$zabbix_object\" -i -";
  88.     }
  89.     for my $line ( @zabbix_queue) {
  90.         print $ZS $line . "\n";
  91.     }
  92.     close $ZS;
  93. }
  94.  
  95. sub print_json_result($$) {
  96.     my $data = $_[0];
  97.     my $key = $_[1];
  98.     my $result = sprintf "{\n";
  99.     $result .= sprintf "\t\"data\":[\n\n";
  100.  
  101.     $result .= join (
  102.         "\t,\n",
  103.         map(
  104.             { sprintf ( "\t{\n\t\t\"\{\#%s\}\":\"%s\"\n\t}\n", $key, $_) }
  105.             @{ $data }
  106.         )
  107.     );
  108.  
  109.     $result .= sprintf "\n\t]\n"
  110.         . sprintf "}\n";
  111.  
  112. }
  113.  
  114. ################################################################################
  115.  
  116. # временные таблицы сервера zabbix. Информация в них хранится до тех пор, пока не будет передана на master node
  117. # для них проверяем самую старую строку
  118. my %tables_sync=(
  119.     'history_sync'  => { time => 'clock', sort => 'id', },
  120.     'history_uint_sync' => { time => 'clock', sort => 'id', },
  121.     'history_str_sync'  => { time => 'clock', sort => 'id', },
  122. );
  123.  
  124. # постоянные таблицы
  125. # для них будем проверять самую новую строку
  126. my %tables=(
  127.  
  128.     'alerts'    => { time => 'clock', sort => 'alertid' },
  129.     'history_log'   => { time => 'clock', sort => 'id' },
  130.     'history_text'  => { time => 'clock', sort => 'id' },
  131.     'events'    => { time => 'clock', sort => 'eventid' },
  132.     'acknowledges'  => { time => 'clock', sort => 'acknowledgeid' },
  133.     'auditlog'  => { time => 'clock', sort => 'auditid' },
  134. #   'auditlog_details'  => { time => 'clock', sort => 'auditdetailid' },
  135.     'service_alarms'    => { time => 'clock', sort => 'servicealarmid' },
  136. );
  137.  
  138. # лучше было бы использовать POSIX::strptime, но его ещё установить нужно
  139. sub ut2str($) {
  140.     my @tmp_time=(localtime $_[0])[5,4,3,2,1,0];
  141.     $tmp_time[0]+=1900;
  142.     $tmp_time[1]+=1;
  143.     return sprintf("%.4d/%.2d/%.2d %.2d:%.2d:%.2d", @tmp_time);
  144. }
  145.  
  146. # выдаёт время из самой старой строки в таблице
  147. # используется для оценки таблиц, отмеченных флагом ZBX_HISTORY_SYNC
  148. # параметры:
  149. #  - DB handle
  150. #  - имя таблицы
  151. #  - имя поля, которое содержит время
  152. #  - имя поля, по которому происходит сортировка
  153. sub get_first_row_timestamp($$$$){
  154.     my ($dbh, $table, $field, $sort) = @_;
  155.     # TODO: исправить
  156.     my $result=time();
  157.  
  158.     # добавить ограничение по node id
  159.     my $sth=$dbh->prepare("select $field from $table where nodeid=$check_node_id order by $sort limit 1;");
  160.     $sth->execute();
  161.  
  162.     my $data = $sth->fetchrow_hashref();
  163.  
  164.     if ( $data ) {
  165.         $result = $data->{$field};
  166.     }
  167.     return $result;
  168.     printf "%s\n", Dumper($sth->fetchrow_hashref());
  169. };
  170.  
  171. # число строк в таблице
  172. # параметры:
  173. #  - DB handle
  174. #  - имя таблицы
  175. sub get_row_count($$){
  176.     my ( $dbh, $tablename ) = @_;
  177.     my $result = 10**10;
  178.  
  179.     my $sth=$dbh->prepare("select count(*) from $tablename;");
  180.     $sth->execute();
  181.  
  182.     my $data = $sth->fetchrow_hashref();
  183.  
  184.     if ( $data and exists $data->{'count'} ) { $result=$data->{'count'} };
  185.  
  186.     return $result;
  187. }
  188.  
  189. # возвращает фильтр, который ограничит область запроса одной нодой
  190. # параметры:
  191. #  - название поля, по которому происходит фильтрация
  192. #  - идентификатор ноды
  193. sub node_filter($$) {
  194.     my ( $column, $nodeid ) = @_;
  195.     my $base = 10**14;
  196.  
  197.     return sprintf ("%s between %s and %s", $column, $nodeid * $base, ( $nodeid + 1 ) * $base - 1) ;
  198. }
  199.  
  200. # выдаёт время самой новой строки в таблице
  201. # используется для таблиц, отмеченных флагом ZBX_HISTORY
  202. # нужно вручную сравнить полученные данные на двух непосредственно связанных нодах ( master node и её дочерняя нода )
  203. sub get_last_row_timestamp($$$$$){
  204.     my ($dbh, $table, $field, $sort, $node) = @_;
  205.  
  206.     my $result=0;
  207.     my $base = 10**14;
  208.  
  209.     # ищем последнюю строку, относящууюся к данной ноде
  210.     #my $sth=$dbh->prepare("select $field from $table order by $sort desc limit 1;");
  211.     my $sth=$dbh->prepare(
  212.         "select max($sort) from $table where "
  213.         . node_filter($sort, $node) . ";"
  214.     );
  215.     $sth->execute();
  216.     my $data = $sth->fetchrow_hashref();
  217.  
  218.     # и из этой последней строки берём время
  219.     if ( $data and exists ( $data->{'max'}) and $data->{'max'} ) {
  220.         $sth = $dbh->prepare(
  221.             "select $field from $table where $sort=" . $data->{'max'}
  222.         );
  223.         $sth->execute();
  224.         $data = $sth->fetchrow_hashref();
  225.  
  226.         if ( $data ) {
  227.             $result = $data->{$field};
  228.         }
  229.     }
  230.     return $result;
  231. };
  232.  
  233. # возвращает список нод ( кроме master node )
  234. # параметры:
  235. #  - DB handle
  236. sub get_nodes($) {
  237.     my $dbh = shift;
  238.     my @nodes = ();
  239.  
  240.     my $sth=$dbh->prepare("select nodeid, masterid from nodes;");
  241.     $sth->execute();
  242.     while ( my $data = $sth->fetchrow_hashref() ) {
  243.         if ( $data->{'masterid'} ) {
  244.             push @nodes, $data->{'nodeid'};
  245.             #printf "%s\n", Dumper( $data->{'nodeid'} );
  246.         }
  247.     };
  248.     return \@nodes;
  249. }
  250.  
  251. # проверяет таблицу, отмеченную флагом ZBX_HISTORY_SYNC.
  252. # параметры:
  253. #  - DB handle
  254. #  - информация о таблице, см. %tables_sync, пример: { 'time' => 'clock', sort => 'id'}
  255. sub check_zhs_table($$$) {
  256.     my ( $dbh, $table, $name ) = @_;
  257.  
  258.     my $lasttime = get_first_row_timestamp($dbh, $name, $table->{'time'}, $table->{'sort'});
  259.     my $now = time();
  260.  
  261.     zabbix_add_to_queue(
  262.         $zabbix_object,
  263.          sprintf( "nodesync.zh_s.oldest.[%s]", $name),
  264.         $now - $lasttime,
  265.     );
  266.  
  267.     #my $row_count = get_row_count($dbh, $name);
  268.     #printf "%s: %i; %i\n", $name, $now - $lasttime, $row_count;
  269. };
  270.  
  271. sub check_zh_table($$$$) {
  272.     my ( $dbh, $tables, $name, $node ) = @_;
  273.  
  274.     my $lasttime = get_last_row_timestamp(
  275.         $dbh,
  276.         $name,
  277.         $tables->{$name}{'time'},
  278.         $tables->{$name}{'sort'},
  279.         $node,
  280.     );
  281.     zabbix_add_to_queue(
  282.         $zabbix_object,
  283.         sprintf( "nodesync.zh.newest.%s.[%s]", $current_node , $name),
  284.         $lasttime,
  285.     );
  286.     #printf "%-20s: %15i; %s\n", $name, $lasttime, ut2str( $lasttime );
  287. };
  288.  
  289. sub usage() {
  290.     printf "-h\t show help\n";
  291.     printf "-n|--dbname <DB name>\n";
  292.     printf "-u|--dbuser <DB user>\n";
  293.     printf "-p|--dbpassword <DB password>\n";
  294.     printf "--dbhost <DB host>\n";
  295.     printf "--dbport <DB port>\n";
  296.     printf "--mode <discover|collect>\n";
  297.     printf "--current_node <master|slave> # mandatory\n";
  298.     printf "--check_node_id <ID> # mandatory\n";
  299.     printf "--check_node_name <host name in zabbix> # mandatory\n";
  300. }
  301.  
  302. # TODO: в перспективе автоматизировать. Для каждого check_node_id получить у пользователя ( и запомнить в storable файле )
  303. # check_node_name и current_node
  304. Getopt::Long::Configure ('bundling');
  305. GetOptions (
  306.     'n|dbname=s'   => \$dbname,
  307.     'u|dbuser=s'   => \$dbuser,
  308.     'p|dbpassword=s'   => \$dbpassword,
  309.     'dbhost=s'   => \$dbhost,
  310.     'dbport=s'   => \$dbport,
  311.     'current_node=s'   => \$current_node, # master | slave
  312.     'check_node_id=s'   => \$check_node_id,
  313.     'check_node_name=s'   => \$zabbix_object,
  314.     'zabbix_server=s'   => \$zabbix_server,
  315.     'mode=s'   => \$mode,
  316.     'h' => \$help,
  317. );
  318.  
  319. if ( $help  ) {
  320.     usage();
  321.     exit 0;
  322. };
  323.  
  324. if ( ! $current_node or ! $check_node_id or ! $zabbix_object ) {
  325.     usage();
  326.     exit 0;
  327. }
  328.  
  329. sub collect() {
  330.     #
  331.     # подключаемся к БД
  332.     #
  333.     my $connect_string = $dbhost ? "dbi:Pg:dbname=$dbname;host=$dbhost;port=$dbport" : "dbi:Pg:dbname=$dbname";
  334.     my $dbh;
  335.     eval {
  336.         $dbh = DBI->connect($connect_string,
  337.             $dbuser, $dbpassword,
  338.             {
  339.                 AutoCommit  => 0,
  340.                 RaiseError  => 0,
  341.             }
  342.         );
  343.     };
  344.  
  345.     if ( ! $dbh ) {
  346.         print "can't connect to DB\n";
  347.         exit 1;
  348.     };
  349.  
  350.     if ( $current_node eq 'slave' ) {
  351.         #
  352.         # проверяем все ZABBIX_HISTORY_SYNC для выбранной ноды
  353.         #
  354.  
  355.         # проверяем ZBX_HISTORY_SINC таблицы.
  356.         # Они говорят сами за себя, сравнивать их с другими нодами не нужно
  357.         # все строки, что в них есть, ждут отправки на master node
  358.  
  359.         for my $name ( keys %tables_sync ) {
  360.             check_zhs_table($dbh, $tables_sync{$name}, $name);
  361.         }
  362.     }
  363.  
  364.     #
  365.     # проверяем все ZABBIX_HISTORY для выбранной ноды
  366.     #
  367.  
  368.     # проверяем ZBX_HISTORY таблицы. Из них берём информацию по отдельным нодам,
  369.     # сравниваем данные ноды с данными её непосредственного master node
  370.  
  371.     for my $name ( sort keys %tables ) {
  372.         check_zh_table($dbh, \%tables, $name, $check_node_id);
  373.     }
  374.  
  375.     $dbh->disconnect();
  376.     zabbix_bulk_send();
  377. };
  378.  
  379. sub discover() {
  380.     # ZBX_HISTORY_SYNC
  381.     my @zbx_history_sync_tables = keys( %tables_sync );
  382.     zabbix_directly_send( "nodesync.zh_s.discover",  print_json_result(\@zbx_history_sync_tables, "ENTITY"));
  383.  
  384.     # ZBX_HISTORY
  385.     my @zbx_history_tables = keys( %tables );
  386.     zabbix_directly_send( "nodesync.zh.discover",  print_json_result(\@zbx_history_tables, "ENTITY"));
  387.     for my $name ( sort keys %tables ) {
  388.         #check_zh_table($dbh, \%tables, $name, $check_node_id);
  389.     }
  390. };
  391.  
  392. if ( $mode eq 'collect' ) {
  393.     collect();
  394. } elsif ( ( $mode eq 'discover') and ( $current_node eq 'master' ) ) {
  395.     discover();
  396. }
  397. exit 0;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement