Ad Widget

Collapse

Perl Zabbix API: всё одной командой и море документаци

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • DRVTiny
    Senior Member
    • Sep 2011
    • 162

    #1

    Perl Zabbix API: всё одной командой и море документаци

    Zabipi - это простая реализация Zabbix API на языке Perl
    Почему Zabipi лучше конкурентов?
    - Название команд и синтаксис параметров соответствует официальной документации разработчиков Zabbiх. Zabipi не пытается научить вас новой "идеологии" и не делает никаких хитрых трюков, а вместо этого просто "переводит" описание Zabbix API на язык Perl наиболее незатейливым и прямолинейным образом:
    Code:
    die 'Cant modify proxy settings for host' unless
     zbx('host.update',{'proxy_hostid'=>11333});
    - Развитые возможности отладки, настраиваемый формат отладочного вывода, наличие режима "DryRun" (включается флагом flDryRun)
    - Zabipi логичным образом дополняет возможности Zabbix API, не отступая ни на йоту от буквы и духа оф.документации:
    Code:
    zbx('item.get',{
      'search'=>{'key'=>'vfs.fs.*'},
      'expandNames'=>1,
      'output'=>['name'],
     }
    );
    В данном случае добавлен отсутствующий в официальной реализации Zabbix API параметр expandNames, позволяющий видеть в имени итема реальные значения вместо "$1", "$2" и т.п.
    Также в Zabipi есть команда queue.get для того, чтобы вы в своём Perl-коде могли продуктивно поработать над "очередью" item'ов, которая есть в интерфейсе Zabbix, но которую невозможно (было до сих пор) получить посредством Zabbix API
    - Zabipi поставляется с массой примеров, от совершенно элементарных до весьма сложных, заслуживающих собственного репозитория на github
    - Zabipi активно поддерживается и постоянно улучшается: вы всегда можете написать мне и быстро получить исчерпывающий ответ или даже инспирировать добавление нового функционала в Zabipi!
    Контакты для оперативной связи со мной:
    email: (drvtiny на гугл почте),
    skype: konovalov-aa

    Пример использования Zabipi:
    Получить все последние итемы хоста с известным наперёд именем:
    Code:
    use Monitoring::Zabipi qw(zbx);
    
    Monitoring::Zabipi->new('mon.corp.ru',{debug=>'true'});
    zbx('auth','Admin','PASSWORD');
    my $iv=zbx('item.get',{
     'host'=>'HOSTNAME',
     'output'=>'extend',
     'monitored'=>'true'
                          });
    
    foreach my $item (@$iv) {
     print <<EOITEM;
    ===========================
    Name: $item->{name}
    Last value: $item->{lastvalue}
    Last clock: $item->{lastclock}
    ===========================
    
    EOITEM
    }
    Не правда ли этот код весьма прост?
    При этом иcчерпывающей документацией к этому примеру является: описание метода item.get.
    Кстати, в zbx('auth',...) можно использовать не только полный URL, включающий загадочное "api_jsonrpc.php", но и просто имя хоста zabbix-фронтэнда (именно фронтэнда, а не сервера!) - вроде бы незначительное улучшение относительно официального API, а всё же полезно и приятно
    Last edited by DRVTiny; 26-12-2014, 15:48. Reason: документаци -> документациИ
  • DRVTiny
    Senior Member
    • Sep 2011
    • 162

    #2
    Sometimes it may be too perlish

    Вывести id всех хостов, содержащих итемы, у которых имя ключа имеет вид: "^.*quality.param.*$" (оно же LIKE '%quality.param%')

    Code:
    print join ("\n",map { $_->{'hostid'} } @{ zbx('host.get', { 'output'=>['hostid'],'itemids'=>[ map { $_->{'itemid'} } @{ zbx('item.get',
    {
     'search'=>{ 'key_'=>'quality.param' },
     'monitored'=>'true',
     'output'=>['itemid']
    }              ) } ]
                    }
                      ) }
                 )."\n";
    Last edited by DRVTiny; 04-04-2014, 14:25.

    Comment

    • DRVTiny
      Senior Member
      • Sep 2011
      • 162

      #3
      Добавил полезные примеры на GitHub (собственно, то, что я сам писал по случаю, но может пригодиться всем).

      Comment

      • DRVTiny
        Senior Member
        • Sep 2011
        • 162

        #4
        Теперь можно сделать так, чтобы вызов Zabbix API приводил к die с внятным сообщение об ошибке в случае, если "что-то пошло не так"
        Пример:

        Code:
        $tmplID=zbx('template.create',{
                             'host'=>$tmplName,
                             'name'=>$tmplDesc || $tmplName,
                             'groups'=>{'groupid'=>$hgid}
                                                        },
                            {'flDieOnError'=>1})->{'templateids'}[0];
        В этом случае вам не нужно обрабатывать ситуацию "template не создан из-за того, что в его техническом имени были переданы символы кириллицы" или "в $hgid содержится null, поэтому permission denied"- достаточно просто указать соотв. флаг. После этого вы будете знать наперёд, что если переменной $tmplID что-то присвоилось, а код продолжает исполняться, то это значит, что всё ОК и в $tmplID корректные данные.

        Comment

        • ArtemK
          Senior Member
          • May 2013
          • 232

          #5
          Судя по всему вы проделали огромную работу, на cpan не было задумок выложить код?

          Comment

          • DRVTiny
            Senior Member
            • Sep 2011
            • 162

            #6
            Модуль очень компактный и немудрёный, никаких лишних сущностей - только оригинальный API в удобной оболочке. На cpan его скоро выложу, вместе с модулем getopt::longwithusage. Последний - крохотная надстройка над обычным getopt::long для реализации автоматически формируемого usage-сообщения. Таких модулей на cpan много, но все они делают много лишнего и не дают возможность удобно передавать списки в качестве параметров (-s Host Login Password, например). Собственно, это нужно для утилит, входящих в поставку Zabipiв качестве "примеров".

            Comment

            • Jimson
              Senior Member
              • Jan 2008
              • 1327

              #7
              На мой субъективный взгляд код нужно во первых "прорядить", сейчас это "стена кода" (пустые строки в коде это как знаки препинания), отступы сделать нормальными - 4 пробела без сворачивания в табуляции, функций типа zbx() ("а тут собственно все что я хотел сделать") быть не должно.

              Впрочем у меня вообще большие сомнения что API забикса можно и нужно прятать в какой то языковой обертке, мне хватает маленького класса у которого есть конструктор (создает LWP::au и логинится) и метода аля query выполняющего запрос, вся логика остается в приложении.

              Comment

              • DRVTiny
                Senior Member
                • Sep 2011
                • 162

                #8
                Пример:
                Прицепить шаблон ко всем перечисленным хостам. Хосты можно указывать по маске ('This is a *ost Nam*').

                Code:
                #!/usr/bin/perl -CDA
                use strict;
                use utf8;
                use constant {
                     SETENV_FILE=>'setenv.conf',
                     TIMEZONE=>'MSK',
                };
                my %SETENV;
                BEGIN {
                 open (FH,'<',substr($0,0,rindex($0,'/')).'/'.SETENV_FILE) || die 'Cant set environment: '.SETENV_FILE.' not found!';
                 %SETENV=map { chomp; $_=~m/^\s*(?<KEY>[A-Za-z0-9_-]+)\s*=\s*(?:(?<QUO>['"])(?<VAL>[^\g{QUO}]+?)\g{QUO}|(?<VAL>[^'"[:space:]]+?))\s*$/?($+{'KEY'},$+{'VAL'}):(
                 push @INC,split(/\;/,$SETENV{'PERL_LIBS'}) if $SETENV{'PERL_LIBS'};
                 close(FH);
                }
                use Monitoring::Zabipi qw(zbx zbx_last_err);
                no warnings;
                
                my %FE;
                @FE{('login','server','pass')}=@SETENV{('ZBX_LOGIN','ZBX_HOST','ZBX_PASS')};
                
                my $dbg={};
                my $firstarg=shift;
                if ($firstarg eq '-x') {
                 $dbg={'debug'=>1};
                } else {
                 unshift @ARGV,$firstarg;
                }
                 
                Monitoring::Zabipi->new($FE{'server'},$dbg);
                zbx('auth',$FE{'login'},$FE{'pass'}) || die 'Oh, shit, i cant authorize you on '.$FE{'server'}."\!\n";
                
                my $tmpls=[{'templateid'=>zbx('template.get',{'search'=>{'name'=>shift}})->[0]{'templateid'}}];
                my $hostids=[ keys { map { map {$_->{'hostid'}=>1} @{zbx('host.get',{'search'=>{'name'=>$_},'searchWildcardsEnabled'=>1,'output'=>['hostid']})} } @ARGV } ];
                
                zbx('template.massadd',
                     {
                       'templates'=>$tmpls,
                       'hosts'=>$hostids,  
                     } 
                   );
                
                zbx('logout');
                Вызов в командной строке:
                Code:
                ./apply_template_to_hosts.pl 'Template App Simpana-p win' OB-ISA OB-WEB1R PG-DB1 PG-DB2R PG-WEB PG-WEBR RCHB-DB1 RCHB-DB1R RCHB-WEB1 RCHB-WEB1R
                Last edited by DRVTiny; 12-08-2014, 11:20.

                Comment

                • DRVTiny
                  Senior Member
                  • Sep 2011
                  • 162

                  #9
                  1) Добавил флаг "wildcards", который позволяет по умолчанию включать searchWildсardsEnabled на всех search'ах:
                  Code:
                  Monitoring::Zabipi->new('mon.corp.ru',{'wildcards'=>'true'});
                  2) Добавил "болванку" prologue.pl, в которую можно просто вписать код, использующий Zabipi.
                  Для работы этого кода нужен файл setenv.conf, в котором содержатся настройки коннекта к "веб-морде" Zabbix (как мы все знаем, API полностью обеспечивает фронтэнд, а не собственно Zabbix-сервер).
                  Last edited by DRVTiny; 02-09-2014, 20:59.

                  Comment

                  • DRVTiny
                    Senior Member
                    • Sep 2011
                    • 162

                    #10
                    Добавил полезную опцию flDbgResultAsListSize для того, чтобы в отладке API видеть только размер списка result, а не само его содержимое.

                    Comment

                    • DRVTiny
                      Senior Member
                      • Sep 2011
                      • 162

                      #11
                      Добавил веб-метод queue.get, позволяющий получить содержимое очереди ожидающих итемов.

                      В качестве примера использования этого метода добавил detect_problem_hosts.pl в examples/

                      Думаю над тем, чтобы добавить получение png-картинок графиков, хотя не факт, что это кому-нибудь нужно.

                      Comment

                      • DRVTiny
                        Senior Member
                        • Sep 2011
                        • 162

                        #12
                        Убрал из кода API-функции zbx потенциально бажный switch, заменил его на экспериментальный (но стабильный и поддерживамый официально разработчиками Perl) given
                        Last edited by DRVTiny; 26-12-2014, 15:45.

                        Comment

                        • DRVTiny
                          Senior Member
                          • Sep 2011
                          • 162

                          #13
                          Функции apiinfo.version и getVersion теперь работают корректно.
                          Code:
                          say zbx('apiinfo.version',[])
                          say zbx('getVersion');
                          say zbx('getVersion',{'flDebug'=>1});
                          say zbx('apiinfo.version',[],{'flDebug'=>1})
                          Но поскольку операция получения версии API в любом случае выполняется самим конструктором new, рекомендуется пользоваться функцией zbx_api_version - это существенно быстрее

                          Code:
                          say zbx_api_version();

                          Comment

                          • DRVTiny
                            Senior Member
                            • Sep 2011
                            • 162

                            #14
                            Добавил флаг flPrettyJSON, облегчающий отладку:
                            Code:
                            Monitoring::Zabipi->new('zabbix.example.com',{'wildcards'=>'true'});
                            zbx('auth','Mylogin','MyP~ssw0rd') || die 'Auth on zabbix.example.com failed";
                            
                            my $user=zbx('user.get',
                             {
                               'search'=>{'alias'=>'BorisKurakin'},
                               'selectMedias'=>'extend',
                               'getAccess'=>'extend',
                             },
                             {
                               'flDebug'=>1,
                               'flPrettyJSON'=>1,
                             }
                            )->[0];
                            А в ответ вы получите вот такой отладочный вывод, который и просили, указывая flDebug=>1 в параметрах метода user.get:
                            Code:
                            JSON request:
                            {"auth":"48ba17a65e0f17dd88da6f261cf96e01","params":{"search":{"alias":"BorisKurakin"},"getAccess":"extend","searchWildcardsEnabled":1,"selectMedias":"extend","selectMediatypes":"extend"},"jsonrpc":"2.0","id":1,"method":"user.get"}
                            ** POST http://zabbix.example.com/zabbix/api_jsonrpc.php ==> 200 OK
                            Decoded content from POST:
                            {
                               "jsonrpc" : "2.0",
                               "id" : 1,
                               "result" : [
                                  {
                                     "medias" : [
                                        {
                                           "mediaid" : "191",
                                           "period" : "1-7,00:00-24:00",
                                           "mediatypeid" : "7",
                                           "userid" : "132",
                                           "active" : "0",
                                           "severity" : "16",
                                           "sendto" : "[email protected]"
                                        },
                                        {
                                           "mediaid" : "192",
                                           "period" : "1-7,09:00-24:00",
                                           "mediatypeid" : "8",
                                           "userid" : "132",
                                           "active" : "0",
                                           "severity" : "16",
                                           "sendto" : "79117777777"
                                        }
                                     ],
                                     "users_status" : "0",
                                     "debug_mode" : "0",
                                     "gui_access" : "0",
                                     "mediatypes" : [
                                        {
                                           "exec_path" : "alert_email",
                                           "status" : "0",
                                           "description" : "EX_Email_Alert",
                                           "username" : "",
                                           "passwd" : "",
                                           "smtp_helo" : "",
                                           "mediatypeid" : "7",
                                           "smtp_server" : "",
                                           "gsm_modem" : "",
                                           "smtp_email" : "",
                                           "type" : "1"
                                        },
                                        {
                                           "exec_path" : "alert_sms",
                                           "status" : "0",
                                           "description" : "EX_SMS_Alert",
                                           "username" : "",
                                           "passwd" : "",
                                           "smtp_helo" : "",
                                           "mediatypeid" : "8",
                                           "smtp_server" : "",
                                           "gsm_modem" : "",
                                           "smtp_email" : "",
                                           "type" : "1"
                                        }
                                     ],
                                     "userid" : "132",
                                  }
                               ]
                            }
                            Если бы вы не передали в параметрах запроса user.get флаг flPrettyJSON, то получили бы ответ от Zabbix-сервера в таком же "плоском виде" (одной длинной строкой), как и перекодированный в JSON текст запроса выше.

                            Данная возможность была реализована как отключённый по умолчанию дополнительный флаг в связи с тем, что во многих случаях многострочный JSON будет загромождать отладочный вывод, делая его слишком объёмным.
                            Last edited by DRVTiny; 26-12-2014, 15:44.

                            Comment

                            • DRVTiny
                              Senior Member
                              • Sep 2011
                              • 162

                              #15
                              Добавил параметр selectRights в метод usergroup.get
                              Оригинальный API к сожалению, не умеет запрашивать права usergroup , это официальная "бага" критического уровня, которую так никто и не удосужился исправить.

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

                              dbDSN=>Строка_Подключения
                              dbLogin=>Логин_СУБД
                              dbPass=>Пароль_СУБД

                              Пример использования (построен на базе стандартного prologue.pl в поставке Zabipi):
                              Code:
                              #!/usr/bin/perl -CDA
                              use strict;
                              use utf8;
                              use constant {
                                   SETENV_FILE=>'setenv.conf',
                                   TIMEZONE=>'MSK',
                              };
                              my %SETENV;
                              
                              BEGIN {
                               open (FH,'<',substr($0,0,rindex($0,'/')).'/'.SETENV_FILE) || die 'Cant set environment: '.SETENV_FILE.' not found!';
                               %SETENV=map { chomp; $_=~m/^\s*(?<KEY>[A-Za-z0-9_-]+)\s*=\s*(?:(?<QUO>['"])(?<VAL>[^\g{QUO}]+?)\g{QUO}|(?<VAL>[^'"[:space:]]+?))\s*$/?($+{'KEY'},$+{'VAL'}):('NOTHING','NOWHERE') } grep { $_ !~ m/^\s*(?:#.*)?$/ } <FH>;
                               push @INC,split(/\;/,$SETENV{'PERL_LIBS'}) if $SETENV{'PERL_LIBS'};
                               close(FH);
                              }
                              
                              use POSIX qw(strftime);
                              use Date::Parse qw(str2time);
                              use Monitoring::Zabipi qw(zbx zbx_last_err);
                              no warnings;
                              use Data::Dumper;
                              
                              my %FE;
                              @FE{('login','server','pass')}=@SETENV{('ZBX_LOGIN','ZBX_HOST','ZBX_PASS')};
                              
                              my $firstarg=shift;
                              my $apiPars={'wildcards'=>'true',
                                           'dbDSN'=>join('','dbi:mysql:database=',$SETENV{'MYSQL_DB'},';host=',$SETENV{'MYSQL_HOST'}),
                                           'dbLogin'=>$SETENV{'MYSQL_USER'},
                                           'dbPass'=>$SETENV{'MYSQL_PASS'},
                                          };
                              if ($firstarg eq '-x') {
                               $apiPars->{'debug'}=1;
                              } else {
                               unshift @ARGV,$firstarg;
                              }
                              
                              Monitoring::Zabipi->new($FE{'server'},$apiPars);
                              zbx('auth',$FE{'login'},$FE{'pass'}) || die 'Oh, shit, i cant authorize you on '.$FE{'server'}."\!\n";
                              
                              print Dumper(zbx('usergroup.get',{'selectRights'=>1}));
                              
                              END {
                               zbx('logout');
                              }

                              Comment

                              Working...