Zabbix API example: creating a graph with LLD-generated items

We talked about the second most-voted feature request for Zabbix, an ability to create a graph containing an item for all LLD-created entities like network interfaces or filesystems. There was a teaser on using the Zabbix API to implement something like that. Now is the time to check out a simple example that shows such a functionality using a Perl module for the Zabbix API, Zabbix::Tiny.

Don’t despair if you have never used Zabbix::Tiny or even Perl before – everybody starts that way, and questions are always welcome. You might also want to review the posts that introduced Zabbix::Tiny:

Our script will do these things:

  • connect to the Zabbix API and obtain the host ID
  • query items by the specified key pattern that belong to the specified host (using the host ID obtained earlier)
  • create a custom graph

Our goal is to automatically create graphs with LLD-generated items like these:
Zabbix graph configuration, showing 4 free diskspace items

We will look at portions of the example script. If you prefer looking at the complete thing, here’s the full script in the Zabbix::Tiny repository.

It all starts with laying down basic rules and importing modules. If these are new concepts for you, refer to the introductory posts linked above.

use strict;
use warnings;
 
use Zabbix::Tiny;
use Getopt::Long qw(GetOptions);
use IO::Socket::SSL;

Then we specify connection parameters – username, password, base URL for the Zabbix API. There’s also a colours array here – these are distinguishable, hardcoded colours that will be used for the graph items. Notice how we have 16 colours defined.

# This example accepts host name and key with wildcards and creates a graph. This would be useful, for example,
#  to create a graph that includes all items that an LLD rule has created for a specific discovered entity.
 
my $username = 'Admin';
my $password = 'zabbix';
my $zserver='http://zabbix_server/zabbix';
my @colours = qw(1A7C11 F63100 2774A4 A54F10 FC6EA3 6C59DC AC8C14 611F27 F230E0 5CCD18 BB2A02 5A2B57 89ABF8
     7EC25C 274482 2B5429);

Next up – defining a few variables and explaining the usage. Let’s talk a bit more about the parameters for this script. It currently accepts 3 parameters:

  • host
  • item key pattern
  • graph name

Host is quite straightforward – it is the hostname (not the visible name) of the host we want to have the graph created for. Graph name is simple as well – literal name for the graph to create. The item key pattern has a small trick up its sleeve – it supports a wildcard of *. This is useful if you have, for example, diskspace items vfs.fs.size[/filesystem,free] and vfs.fs.size[/filesystem,used] for various filesystems. It wouldn’t be very useful to add all of these items to a single graph. Adding all the free items would be better, and that could be done by specifying the key pattern as vfs.fs.size[*,free]. The asterisk acts like a standard wildcard here.

my $itemkey;
my $host;
my $graphname;
my @gitems;
my $result;
my $help;
my $usage = <<"USAGE"; $0 --host "HOST" --key "ITEM_KEY" --graphname "GRAPH_NAME"
 
Will create a custom graph with items that match the passed key on the specified host
 
Example: perl $0 --key "vfs.fs.size[*,free]" --host "A test host" --graphname "Free diskspace"
The above would find items like vfs.fs.size[/,free], vfs.fs.size[/boot,free], vfs.fs.size[/home,free] and so on
USAGE
 
GetOptions(
    'key|k=s'       => \$itemkey,
    'host|h=s'      => \$host,
    'graphname|n=s' => \$graphname,
    'help'          => \$help,
) or die "Usage:\n$usage";
 
if ($help) {
    print "Usage:\n$usage";
    exit;
}
 
if (not $itemkey) {
    die "specify item key : --key";
}
 
if (not $host) {
    die "specify host name : --host";
}
 
if (not $graphname) {
    die "specify graph name : --graphname";
}

The we connect to the Zabbix API and get the ID of the specified host. We will need it to find items that should be included in the graph.

my $url = $zserver . "/api_jsonrpc.php";
my $zabbix = Zabbix::Tiny->new(
    server   => $url,
    password => $password,
    user     => $username,
    ssl_opts => {
        verify_hostname => 0,
        SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE,
    }
);
 
print "getting host ID of host '$host'\n";
my $hostid = $zabbix->do(
    'host.get',
    {
        output => "hostid",
        filter => { "host" => $host },
    },
);
if (@$hostid) {
    print "... host exists\n";
}
else {
    die "host '$host' does not exist on $zserver";
}

With the host ID on hand, we search for items, limiting to that host ID and searching by an item key pattern. There’s one small trick here – we have enabled the searchWildcardsEnabled flag, which makes the asterisk a wildcard symbol.

print "gathering item IDs\n";
$result = $zabbix->do(
    'item.get',
    {
        output                 => "itemid",
        hostids                => [ $hostid->[0]->{hostid} ],
        search                 => { key_ => $itemkey },
        searchWildcardsEnabled => 1,
    },
);

Then we parse the returned item data – we only need the item IDs. itemids array is populated by extracting the value of the matching hash key itemid. If the usage of the map function is confusing, there’s also a simple loop to demonstrate what the map function does.

my @itemids = map($_->{itemid}, @$result);
# The line above is equivalent to:
#my @itemids;
#foreach (@$result) {
#    push @itemids, $_->{itemid}
#}
my $itemcount = scalar @itemids;
 
if (!@itemids) {
    die "found no items matching key '$itemkey' on host '$host'";
}
print "... got " . $itemcount . " itemids\n";

Here we use the colour array, defined above. Each item we found is added to the graph and gets a colour from that array. Once we know all the items that should be added to the graph, graph is created with hardcoded width of 900 and height of 200. Note that the width parameter for normal graphs is ignored by Zabbix – graphs auto-scale to the browser window width.

foreach my $index (0..$#itemids) {
    push @gitems, { itemid => $itemids[$index], color => $colours[$index] };
};
 
eval { $result = $zabbix->do(
    'graph.create',
    {
        name   => $graphname,
        width  => '900',
        height => '200',
        gitems => [ @gitems ],
    },
) };
print $@ if ($@);

Once we are done, convenience links are provided to view and configure the created graph.

print "View the created graph: " . $zserver . "/charts.php?graphid=" . $result->{graphids}[0] . "\n";
print "Configure the created graph: " . $zserver . "/graphs.php?form=update&graphid="
    . $result->{graphids}[0] . "\n";

Here’s a successful run of the script that has found 4 items:

> perl example_create_lld_entity_graph.pl --key 'vfs.fs.size[*,free]' \
--host "A test host" --graphname "Free diskspace"
getting host ID of host 'A test host'
... host exists
gathering item IDs
... got 4 itemids
View the created graph: http://zabbix_server/zabbix/charts.php?graphid=652
Configure the created graph: http://zabbix_server/zabbix/graphs.php?form=update&graphid=652

Now that we have discussed all the parts, check out the full script.

Please note that this is not a full solution that will create all the graphs you might want to have. It doesn’t work across multiple hosts and cannot find all discovered items among other things. So here’s a list of…

Possible improvements

  • Allow to specify host group to create such graph on all hosts
  • Add error checking
  • Check for a large number of items – there are only 16 colours defined, and it might make sense to either disable graph legend or refuse to create graph if a huge number of items is found
  • For a large number of items modify the palette automatically to get many unique colours
  • Allow to modify other graph properties – width, height, working time and trigger displaying, supporting stacked graphs and so on
  • Create graphs for all found item prototypes
  • Allow to specify blacklist/whitelist for the item key patterns

Leave a Reply