Using the Zabbix::Tiny to change an item interval based on a trigger.

A relatively common Zabbix feature request is to change the interval of a Zabbix item (how often the item is updated) based on the value of the item.  This post will illustrate how to use a trigger to execute a Perl script to meet this goal.

In this example, the item will be a net.tcp.service[] item to check a web server for an http response. The goal is to have this service checked every 5 minutes (300 seconds) under normal conditions, but if the response indicates that the service is down, change the interval to 60 seconds, which will allow the trigger to get cleared faster.  Additionally, once the service is back up, the interval should return to 5 minutes.

Item Configuration

my_web_service_5001_itemconfig
Item Configuration

As stated above,  this example item will be a net.tcp.service item.  In this case the monitored service runs on the Zabbix server itself.  The important part of this configuration, as it relates to this article, is to set the interval to the value you would like for the OK state. Remember, in this example, when the trigger that checks this item is OK, it should only be checked every 5 minutes (300 seconds).

Trigger Configuration

Trigger Configuration
Trigger Configuration

Because we want this action to occur immediately when the web service is detected as down, we create a trigger using the last() function. The example trigger name is My Web Service 5001 Down, which is matched in the action configuration below. There’s not a lot of interest here, aside from the fact that 1 is “up” and 0 is “down”.

Action Configuration

A dedicated action is configured for this example with the following criteria:

  1. web_service_5001_down_actionactionconfigAction name is “Web Service 5001 Down”.
  2. The Subject and Message can stay at the default values because the action as configured here does not send any alert messages.
  3. The action conditions are as follows. It is important NOT to include a condition for Trigger value = Problem, because we want the operation to occur both when the trigger fires (to set the interval to 60 seconds) as well as when the trigger returns to the OK state (to set the interval to 300 seconds):
    1. Maintenance status not in maintenanceweb_service_5001_down_actionconditionconfig
    2. Trigger name like My Web Service 5001 Down. (Matches the name of the trigger)
  4. The action’s Operations configuration is to “Run remote commands on current host”. Set:
    1. The steps to 1 and 1 (the operation occurs immediately and only once).
    2. The operation type to Remote command.
    3. web_service_5001_down_actionoperationsconfigAdd Current host to the target list.
    4. Choose to execute on Zabbix server.
    5. Set the Command to:
      /etc/zabbix/alertscripts/zabbix_interval.pl "{TRIGGER.STATUS}" "{ITEM.ID}" "300" "60"

zabbix_interval.pl Arguments

The command here is a script that I have stored in /etc/zabbix/alertscripts/zabbix_interval.pl on my machine. The script itself is below, but make sure that this path points to wherever you have the script saved. This script will take four arguments:

  1.  {TRIGGER.STATUS} – This is macro is populated by the Zabbix  server when the action occurs. Its value will be either OK or PROBLEM.
  2. {ITEM.ID} – This macro is populated by Zabbix server, and is the first item from the trigger expression. In our case (and in most cases) there is only one item evaluated for triggers. If you did have multiple items, and required that the second (or third, etc.) item be adjusted, then you could use a value like {ITEM.ID2}, etc.
  3. [OK Interval] – The desired value of the interval (in seconds) for the item when the trigger is in an OK state. In this example it is set to 300.
  4. [Problem Interval] – The desired value of the interval (in seconds) for the item when the trigger is in a PROBLEM state. In this example it is set to 60.

This means that, when the action occurs for this item, the following will be executed on the Zabbix server (where my Item ID is 15836). There’s no reason for me to have to know this number, as the script will use it to look up the desired item, etc.:

$> /etc/zabbix/alertscripts/zabbix_interval.pl OK 155836 300 60

Zabbix_Interval.pl

Here’s the script itself.  Remember to make it executable by Zabbix, or it will fail to run.

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

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

my $state          = $ARGV[0];
my $itemid         = $ARGV[1];
my %interval;
$interval{OK}      = $ARGV[2];
$interval{PROBLEM} = $ARGV[3];

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

my $update = $zabbix->do(
    'item.update', {
        itemid => $itemid,
        delay  => $interval{$state},
    },
);

Breakdown

Load Modules

Lines 1-5 are the she-bang, and the loading a few basic modules, all of which are covered in my Perl and Zabbix API – Getting Started post:

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

Load Configuration Files

Also, as indicated the aforementioned post it’s a good idea to keep reusable data (particularly password) in an external configuration file. This script is no different, and lines 7 and 8 are defining and loading the configuration:

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

Set variables from arguments

Lines 10-14 set variables from the input. All arguments passed to the script are held in the special variable @ARGV, and are accessed as $ARGV[n], where n is the position of the argument, starting from 0. Thus, $state will be the first argument, $ARGV[0], which can be either OK or PROBLEM, and $itemid will be taken from the second argument, $ARGV[1], which is 155836 in the example shown above.

A hash of %interval is created, and the desired ok interval is assigned to $interval{OK}, while the problem interval is assigned to $interval{PROBLEM}. This provides us with a small ‘hash look up table’, and  we’ll see why this is done later.

my $state          = $ARGV[0];
my $itemid         = $ARGV[1];
my %interval;
$interval{OK}      = $ARGV[2];
$interval{PROBLEM} = $ARGV[3];

Create the Zabbix::Tiny object

The code to create the Zabbix::Tiny object should look the same as the earlier Zabbix::Tiny Simple Usage post. The values are populated from the external configuration .ini file.

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

Update the Item

Here’s the fun part. We call the item.update method of the Zabbix API. We have the itemid because it was passed to the script by the action. The $state key value in $interval{$state} will expand so that if the problem state is OK, then the delay will be set to $interval{OK}, and if the state is PROBLEM, then the delay will be set to $interval{PROBLEM}.

my $update = $zabbix->do(
    'item.update', {
       itemid => $itemid,
       delay  => $interval{$state},
    },
);

Improvements

There are some ways in which this script could be improved, by providing some error checking, adding some logging, providing a few other constraints, and possibly devising a way to look up the desired OK and PROBLEM values from another source, but overall, this provides a fairly neat and simple way to reach the goal of altering properties of the item based on the value and status of the item.

2 thoughts on “Using the Zabbix::Tiny to change an item interval based on a trigger.”

  1. A risky point here that when an item update interval is temporary decreased – the item on graph will show dots (instead of line) for period when trigger of the item was in OK state. It may look not very nice and may mislead …

    1. Thanks for pointing that out. I’ll try to grab a screen shot illustrating this.

      To be honest, changing the interval of an item was a feature I wanted when I first started using Zabbix, but currently I can’t thing of a really strong use case scenario for this. I do frequently execute scripts in this manner (as the operation of an action), but they’re usually of the “get customer information for the system out of the CRM database and email them regarding the trigger”.

      So if i don’t find changing the item interval to be a useful task, why did I write this? I did it because I wanted to do a very simple example for the API usage via Zabbix::Tiny, so that ruled out connecting to an external database, and sending email (not that these are particularly difficult, but the less external interaction the more direct the example). With that in mind, I’d welcome the opportunity to provide example solutions for any other problem that would be similarly scoped tasks if someone suggests them? Perhaps adding an SNMP interface to a host from an Auto-registration action? I’d welcome suggestions.

Leave a Reply