Perl and Zabbix API – Getting Started

Zabbix’s API has the advantage of being both extremely flexible and extremely powerful.  Scripts that leverage the API can address many of the perceived ‘short-comings’, or commonly requested features of Zabbix.  I wrote the Zabbix::Tiny module as a small interface to the API to abstract some of the boiler plate that I was repeating in each script I was writing without it.  Rather than delve directly into the module and it’s uses, I wanted to first cover a few of the dependencies I rely on for (nearly) every script that I write using the Zabbix API. Any articles that I write in the future will consider these points as implied.

"Perl comes off the wall" with a short history of Perl and a portrait of Larry Wall
Perl comes off the wall. Image from eWeek

Shebang and Modern::Perl

As a she-bang line, I prefer #!/usr/bin/env perl to something like #!/usr/bin/perl primarily because as a FreeBSD user the correct path is /usr/local/bin/perl, so the former example enhances portability.

“Modern Perl” can mean several things. It can be referring to a general philosophy of current Perl best practices. It’s also the title of a (highly recommended) book  and website that champions these practices.  It is also the name of a specific Perl module that will set a few simple parameters in a script.

It’s the third usage, the module, that is relative here. In a nutshell it’s like use strict;, use warnings;, and use 5.010;  reduced to a single line.  Combining the shebang and Modern::Perl, means that every script I write starts like this:

#!/usr/bin/env perl
use Modern::Perl;

CPAN and cpanminus

CPAN is the Comprehensive Perl Archive Network. It is one of the largest collections of free software libraries in the world, and is the general standard for distribution of modules for Perl. Cpanminus is a simplified minimalist interface for installing modules from CPAN. As an example, Modern::Perl is not included with perl. It’s a module that can be installed from the CPAN using cpanminus (or other CPAN client if you prefer). It’s quite simple:

#> cpanminus Modern::Perl

Note that there’s still plenty of additional caveats. You’ll need root or sudo if installing modules globally, and perlbrew or local::lib if not. Some distributions favor their own package management systems, especially for modules that are used in core components of the distribution. The aforementioned perlbrew can address this.  I would (again) recommend the Modern Perl book (it’s free!) as a resource for more information.

Zabbix::Tiny

As mentioned above, is a module that I wrote which eliminates some of the repetitive tasks and monotonous code writing when using Perl to interface with the Zabbix API. This module is also available on CPAN. To install it:

#> cpanminus Zabbix::Tiny

Because I use this module for every script I write that interacts with the Zabbix API, these scripts always begin like this:

#!/usr/bin/env perl
use Modern::Perl;
use Zabbix::Tiny;

File Hierarchy

I tend to keep my scripts in a single directory, and then use git and GitLab (GitLab offers free private repositories) to provide version control as well as a rudimentary method of sharing these scripts between hosts. Even though GitLab offers private repositories, I really do not want credentials pushed there – ever. Additionally, I may use the same script against more than one Zabbix server, and not want to have different branches, etc. for each server URL.  To address this, I’ll use configuration files. Here’s the directory hierarchy I use to address this. Assume that my base directory is /usr/home/ben/, I’ll have the following:

A configuration file that holds user credentials, Zabbix server URL(s), and anything else I either want to keep private, or would use in more than one script, /usr/home/ben/globals.conf

I also have a directory that I’ll use to contain my scripts. This is the directory that I’ll track with git.

#> ls /usr/home/ben/scripts/
.git          aggregate_items.pl    ex1.pl
.gitignore    conf/                 ex2.pl

Within the conf/ directory, I’ll have a configuration file for each script (if it needs one):

#> ls /usr/home/ben/scripts/conf/
ex1.conf         ex2.conf
ex1.conf.dist    ex2.conf.dist

The .dist files are just example files showing the options for the script.  There is an entry for conf/*.conf in my .gitignore files so that the actual configuration files don’t get tracked with git, and thus don’t clobber actual script configurations when I pull changes from GitLab.

Config::Tiny

OK – I’ve got configuration files, but how do I read them? Config::Tiny is a module that reads (and writes) .ini style configuration files. I generally use two files per script – global configuration and per-script configuration. Here’s an example of how these look:

#> cat /usr/home/ben/globals.conf
[zabbix]
url=https://zabbix.domain.lan/zabbix/api_jsonrpc.php
user = apiuser
pass = supersecret

#> cat /usr/home/ben/scripts/conf/ex1.conf
[email]
to   = ben@domain.lan
from = zabbix@domain.lan
cc   = someone@domain.lan

Config::Tiny can then be used to set the values into the file in the following manner. Note: since this script doesn’t connect to the Zabbix API, there’s no need to use Zabbix::Tiny

#!/usr/bin/env perl
use Modern::Perl;
use Config::Tiny;

## Set the filenames as variables
my $gbl_file  = "/usr/home/ben/globals.conf";
my $conf_file = "/usr/home/ben/scripts/conf/ex1.conf";

## Use Config::Tiny to read the config files
my $global = Config::Tiny->read($gbl_file);
my $conf   = Config::Tiny->read($cnf_file);

## Assign these to easier to us scalar variables
my $url  = $global->{zabbix}->{url};
my $user = $global->{zabbix}->{user};
my $pass = $global->{zabbix}->{pass};
my $to   = $conf->{email}->{to};
my $from = $conf->{email}->{from};

## Do something with the variables
say "Log into $url as $user";
say "Mail $to from $from";

## Or use the Config::Tiny hashref directly, too.
say "Copy: " . $conf->{email}->{from};

This will produce the following when run:

#> ./ex1.pl
Log into https://zabbix.atgncloud.com/api_jsonrpc.php as apiuser
Mail ben@domain.lan from zabbix@domain.lan
Copy: zabbix@domain.lan

FindBin

FindBin is a Perl core module – this means that you don’t have to install it from CPAN, but you still have to declare use FindBin; in your script. FindBin can be used to find the original directory of the perlscript. This is useful when your configuration file is in a sub directory relative path from your script. For example, if I moved all of these files to a different user directory (/usr/home/whosgonna/ for example), the configuration files would fail to load because they have a hard coded path. The file can be updated to use FindBin and keep relative paths this way. Changes between the previous example and this in bold text:

#!/usr/bin/env perl
use Modern::Perl;
use Config::Tiny;
use FindBin qw($Bin);

## Set the filenames as variables
my $gbl_file  = "$Bin/../globals.conf";
my $conf_file = "$Bin/conf/ex1.conf";

## Use Config::Tiny to read the config files
my $global = Config::Tiny->read($gbl_file);
my $conf   = Config::Tiny->read($cnf_file);

## Assign these to easier to us scalar variables
my $url  = $global->{zabbix}->{url};
my $user = $global->{zabbix}->{user};
my $pass = $global->{zabbix}->{pass};
my $to   = $conf->{email}->{to};
my $from = $conf->{email}->{from};

## Do something with the variables
say "Log into $url as $user";
say "Mail $to from $from";

## Or use the Config::Tiny hashref directly, too.
say "Copy: " . $conf->{email}->{from};

Data::Dumper

Data::Dumper can print out a Perl data structure, making it invaluable for troubleshooting and development purposes.  While most of the scripts that I use don’t require Data::Dumper when complete, in many cases it’s used while writing the script to make sure that the data received is correct. Updating the same script as before:

#!/usr/bin/env perl
use Modern::Perl;
use Config::Tiny;
use FindBin qw($Bin);
use Data::Dumper;

## Set the filenames as variables
my $gbl_file  = "$Bin/../globals.conf";
my $conf_file = "$Bin/conf/ex1.conf";

## Use Config::Tiny to read the config files
my $global = Config::Tiny->read($gbl_file);
my $conf   = Config::Tiny->read($cnf_file);

## Assign these to easier to us scalar variables
my $url  = $global->{zabbix}->{url};
my $user = $global->{zabbix}->{user};
my $pass = $global->{zabbix}->{pass};
my $to   = $conf->{email}->{to};
my $from = $conf->{email}->{from};

## Do something with the variables
say "Log into $url as $user";
say "Mail $to from $from";

## Or use the Config::Tiny hashref directly, too.
say "Copy: " . $conf->{email}->{from};

## Show the data struture of $conf
print Dumper $conf;

Now the script will do the following. I’ve reduced some of the indentation from Data::Dumper to allow the text to better fit the page:

#> ./ex1.pl
Log into https://zabbix.atgncloud.com/api_jsonrpc.php as apiuser
Mail ben@domain.lan from zabbix@domain.lan
Copy: zabbix@domain.lan
$VAR1 = bless( {
          'email' => {
                    'from' => 'zabbix@domain.lan',
                    'to' => 'ben@domain.lan',
                    'cc' => 'someone@domain.lan'
                     }
               }, 'Config::Tiny' );

A few other useful Modules

The above modules and pragmas are in all but the most trivial scripts that I use when connecting to the Zabbix API. There are a few other modules that I commonly use depending on the intent of the script. In no particular order:

Log::Log4perl – I also use this in nearly every non-trivial script. It’s somewhat complex and doesn’t directly impact these scripts, so I’ve omitted it from these examples.

MIME::Lite::TT::HTML – This module can be used to send html (and text) emails using template toolkit for formatting.

DBI – This is the Perl module for abstraction of database access. Generally speaking, you should not be accessing the Zabbix database directly, but I find frequently I’m cross referencing other databases against information in Zabbix.

DateTime – Correct handling of date and time in Perl, including calculation and other datetime math.

List::Util – Contains functions for getting maximum, minimum, sum, product, etc from a list of values.

Summary Script Skeleton

As a summary, here’s a skeleton of what I’d use for scripts using the Zabbix API.

#!/usr/bin/env perl
use Modern::Perl;
use Zabbix::Tiny;
use Config::Tiny;
use FindBin qw($Bin);
use Data::Dumper;

my $gbl_file = "$Bin/../global_configs.conf";
my $global = Config::Tiny->read($gbl_file);

my $zabbix = Zabbix::Tiny->new(
    server   => $global->{zabbix}->{url},
    password => $global->{zabbix}->{pass},
    user     => $global->{zabbix}->{user}
);

 

3 thoughts on “Perl and Zabbix API – Getting Started”

Leave a Reply