Перейти к содержанию

Self-engaging

Welcome to Zabbix API self engaging methods. All upcoming chapters will address the tools available to allow software to re-engage with itself. It is like developing a small service(s) which runs on the top of Zabbix and do exactly the tasks we told to do. This is like simulating an extra employee in the company.

API variables

To start to engage with Zabbix API:

Create a dedicated service user. Go to Users => Users, click Create user, set Username api, install Groups No access to the frontend, Under Permissions tab, assign user role Super admin role which will automatically give user type Super admin.

Create API user

12.1 Create API user

Permissions tab:

API user permissions

12.2 API user role and user type

Under Users => API tokens press New API token, assign user api. We can uncheck Set expiration date and time, press Add. Copy macro to clipboard.

Add token to user object

12.3 Add token to user object

Visit Administration => Macros and install macro. To simulate all upcoming chapters much faster, consider running token in plain text.

Token dafa06e74403ca317112cf5ddd3357b2ad2a2c5cb348665f294a53b4058cfbcf must be placed:

{$ZABBIX.API.TOKEN}

Address https://zabbix.book.the of the frontend server must be used via:

{$ZABBIX.URL}

Later throughout chapters, we will use a reference on the API endpoint in a format of:

{$ZABBIX.URL}/api_jsonrpc.php

Now we can take new 2 variables and install globally:

User macros

12.4 User macros

First API call

If you feel new to Zabbix API, try this curl example from Zabbix frontend server.

Set bash variables:

ZABBIX_API_TOKEN="dafa06e74403ca317112cf5ddd3357b2ad2a2c5cb348665f294a53b4058cfbcf"
ZABBIX_URL="https://zabbix.book.the/api_jsonrpc.php"

This snippet is tested and compatible with version 7.0/7.4:

curl --insecure --request POST \
--header 'Content-Type: application/json-rpc' \
--header 'Authorization: Bearer '$ZABBIX_API_TOKEN \
--data '{"jsonrpc":"2.0","method":"proxy.get","params":{"output":["name"]},"id":1}' \
$ZABBIX_URL

Setting Bearer token in header is available and recommended since 7.0.

Setting a static token is available since 6.0. In version 6.0, the token is not in header, but inside JSON body like this:

curl --insecure --request POST \
--header 'Content-Type: application/json-rpc' \
--data '{"jsonrpc":"2.0","method":"proxy.get","params":{"output":["host"]},"auth":"'"$ZABBIX_API_TOKEN"'","id":1}' \
$ZABBIX_URL

Host group membership (HTTP agent)

Use case

Every time an email arrives user would love to see all host groups the host belongs.


Implementation

Use {HOST.HOST} as an input for the "host.get" API method and find out about host group membership. Format reply in one line, store it in inventory.


An item type "HTTP agent" is fastest way to run a single Zabbix API call and retrieve back result. This is possible since Zabbix 6.0 where configuring a static session token becomes possible. An upcoming solution is tested and works on version 7.0/7.4

Create new HTTP agent

Item name: host.get

Type: HTTP agent

Key: host.get

Type of information: Text

URL:

{$ZABBIX.URL}/api_jsonrpc.php

Request type: POST

Request body type: JSON data

Request body:

{
    "jsonrpc": "2.0",
    "method": "host.get",
    "params": {
        "output": ["hostgroups"],
        "selectHostGroups": "extend",
        "filter": {"host":["{HOST.HOST}"]}
    },
    "id": 1
}

Headers

Name Authorization with value:

Bearer {$ZABBIX.API.TOKEN}

User macros

12.5 Host get method via HTTP agent item

Preprocessing

JSONPath:

$.result[0].hostgroups[*].name

JavaScript:

return JSON.parse(value).join(',');

User macros

12.6 Preprocessing

Last step is to store the outcome in the inventory. Scroll down to the bottom of HTTP agent item and select an inventory field for example "Site rack location".

To access suggested inventory field, we must use:

{INVENTORY.SITE.RACK}

To include extra information inside the message template follow this lead:

Inventory fields in media type

12.7 Inventory fields in media type

Warning

If data collection is done by Zabbix proxy, it is possible the proxy is incapable to reach Zabbix frontend server due to limitation in firewall. Use curl -kL "https://zabbix.book.the" to test!


Auto close problem (Webhook)

Use case

Due to reason of not being able to find a recovery expression for a trigger, need to close the event automatically after certain time.


Implementation

Trigger settings must support Allow manual close. On trigger which needs to be auto closed there must be a tag auto with a value close. An action will invoke a webhook which will use Zabbix API to close event.


To implement, visit Alerts => Scripts, press Create script

Auto close problem

12.13 Auto close problem

Name will be:

Automatically close problem

Scope: Action operation

Type: Webhook

Parameters:

eventid set:

{EVENT.ID}

msg title is:

Auto closed by API

token must be:

{$ZABBIX.API.TOKEN}

url points to:

{$ZABBIX.URL}/api_jsonrpc.php

Script:

var params = JSON.parse(value);

var request = new HttpRequest();
request.addHeader('Content-Type: application/json');
request.addHeader('Authorization: Bearer ' + params.token);

var eventAcknowledge = JSON.parse(request.post(params.url,
    '{"jsonrpc":"2.0","method":"event.acknowledge","params":{"eventids":"'+params.eventid+'","action":1,"message":"'+params.msg+'"},"id":1}'
));

return JSON.stringify(eventAcknowledge);

For the triggers which need to be closed automatically, we need to:

1) Set Allow manual close checkbox ON

Allow manual close

12.8 Allow manual close

2) Install tag auto with value close

Install trigger tags

12.9 Trigger tags

Go to Alerts => Actions => Trigger actions

Create an action which will be targetable by using tag name auto with a tag value close.

Set up conditions for action

12.10 Conditions for action

It's important to not create operation step 1, but start operation with step 2:

A delayed operation

12.11 A delayed operation

The Default operation step duration field will serve the purpose to tell how long the event will be in problem state.

Default operation step

12.12 Close event later

This solution has been tested with 7.0/7.4

Self destructive host (Webhook)

Use case

On a big infrastructure with thousands of devices there is no human who can track which devices are deprovisioned. Need to automatically remove unhealthy devices.


Implementation

Problem events such as "Zabbix agent is not available" or "No SNMP data collection" sitting too long in problem state will invoke a webhook to delete the host.


To implement, visit Alerts => Scripts, press Create script

Auto close problem

12.13 Auto close problem

Name will be:

Delete host

Scope: Action operation

Type: Webhook

Parameters:

hostid set:

{HOST.ID}

token must be:

{$ZABBIX.API.TOKEN}

url points to:

{$ZABBIX.URL}/api_jsonrpc.php

Script:

// delete host via Zabbix API
var params = JSON.parse(value);

var request = new HttpRequest();
request.addHeader('Content-Type: application/json');
request.addHeader('Authorization: Bearer ' + params.token);

var hostDelete = JSON.parse(request.post(params.url,
    '{"jsonrpc":"2.0","method":"host.delete","params":['+params.hostid+'],"id":1}'
));

return JSON.stringify(hostDelete);

To setup action, the trigger must running a tag delete with value host. The conditions can be to target tag plus value and trigger severity:

Delete host action conditions

12.15 Delete host target tag and tag value

Here we are running a delayed action with a step number 31. Because default duration is 1d, the host will be deleted after 30 days.

Delete host action operations

12.16 Delete host operations

Replace host Visible name (Script)

The "Script" item is handy to take data from step 1 and use it as an input for step 2.

Configuration-wise, the "Script" item will give an elegant looking solution where all base variables are kept outside the script.

Use case

Replace host "Visible name" with a data coming to an item


Implementation

Every host, by default, will store the intended host visible name inside inventory. The "Script" item (without running extra API calls) will compare inventory with host visible name. If inventory field is empty, the visible field will not be replaced.


This is maximum efficiency to run a single API call once per day. If nothing needs to be done, API calls will not be wasted. No SQL UPDATE operations for the Zabbix database :)

Name will be:

Delete host

Scope: Action operation

Type: Webhook

Parameters:

hostid set:

{HOST.ID}

token must be:

{$ZABBIX.API.TOKEN}

url points to:

{$ZABBIX.URL}/api_jsonrpc.php

Script:

// load all parameters in memory
var params = JSON.parse(value);

// new API call
var request = new HttpRequest();
request.addHeader('Content-Type: application/json');
request.addHeader('Authorization: Bearer ' + params.token);

// obtain Bare minimum fields: host "Visible name" and inventory "Name"
var hostData = JSON.parse(request.post(params.url,
    '{"jsonrpc":"2.0","method":"host.get","params":{"output":["hostid","inventory","name","host"],"selectInventory":["name"]},"id":1}'
)).result;

var listOfErrors = [];
var listOfSuccess = [];

// iterate through host list
for (var h = 0; h < hostData.length; h++) {

    // validate if inventory "name" element exists
    if (typeof hostData[h].inventory.name !== 'undefined') {

        // if "name" field is not empty
        if (hostData[h].inventory.name.length > 0) {

            // compare if inventory name is not the same as host visible name
            if (hostData[h].inventory.name !== hostData[h].name) {

                Zabbix.Log(params.debug, 'Host visible name field, host: ' + hostData[h].name + ' need to reinstall visible name');

                // formulate payload for easy printing for troubleshoting
                payload = '{"jsonrpc":"2.0","method":"host.update","params":' + JSON.stringify({
                    'hostid':hostData[h].hostid,
                    'name':hostData[h].inventory.name
                    }) + ',"id":1}';

                Zabbix.Log(params.debug, 'Host visible name field, payload: ' + payload);

                try {
                    hostUpdate = JSON.parse(request.post(params.url, payload));

                    // save API errors, like name already exists:
                    if (typeof hostUpdate.error !== 'undefined') { listOfErrors.push({'error':hostUpdate.error,'origin':hostData[h].host}) }

                    // save successfull operation:
                    if (typeof hostUpdate.result !== 'undefined') { listOfSuccess.push(hostUpdate.result) }

                }
                catch (error) {
                    throw 'noo';
                }

            }

        }

    }

}

return JSON.stringify({ 'listOfSuccess': listOfSuccess, 'errors': listOfErrors });

The chances of having duplicate host names are still possible. In this case, the script will continue to parse all hosts and will retry update operation. Ensure $.listOfErrors in output is an empty list.

This is tested and works with Zabbix 7.0

Cleanup unused ZBX interfaces (Webhook)

Use case 1

Default "Host availability" widget will print "Unknown" interfaces if none of Zabbix agent passive checks are using it. Need to remove interface to get "Unknown" interface number closer to 0


Unknown ZBX passive interfaces

12.17 Unknown ZBX passive interfaces

Use case 2

Active checks by design do not require an interface. Having a defined interface will mislead the team to understand how active checks actually works.


Use case 3

https://cloud.zabbix.com/ is good for server monitoring with active checks. While registering new servers, the IP address of host interface is not relatable to infrastructure. Remove the interface to make setup look more clean.


Implementation

To bring aboard a host, run a webhook to validate if an interface is used by any passive Zabbix agent items. If it's not used, then remove interface.


To implement, visit Alerts => Scripts, press Create script

Remove unused ZBX interfaces

12.18 Remove unused ZBX interfaces

Webhook Name will be:

Remove unused ZBX interfaces

Scope: Action operation

Type: Webhook

Parameters:

host set:

{HOST.HOST}

token must be:

{$ZABBIX.API.TOKEN}

url points to:

{$ZABBIX.URL}/api_jsonrpc.php

Set debug points to 4

Script:

// Load all variables
var params = JSON.parse(value);

var request = new HttpRequest();
request.addHeader('Content-Type: application/json');
request.addHeader('Authorization: Bearer ' + params.token);

// Pick up hostid
var hostid = JSON.parse(request.post(params.url,
    '{"jsonrpc":"2.0","method":"host.get","params":{"output":["hostid"],"filter":{"host":["' + params.host + '"]}},"id":1}'
)).result[0].hostid;

// Extract all passive Zabbix agent interfaces
var allAgentInterfaces = JSON.parse(request.post(params.url,
    '{"jsonrpc":"2.0","method":"hostinterface.get","params":{"output":["interfaceid","main"],"filter":{"type":"1"},"hostids":"' + hostid + '"},"id":1}'
)).result;

// If any ZBX interface was found then proceed fetching all items because need to find out if any items use an interface
if (allAgentInterfaces.length > 0) {
    // Fetch all items which are defined at host level and ask which item use passive ZBX agent interface
    // Simple check items (like icmpping) also can use zabbix agent interface
    var items_with_int = JSON.parse(request.post(params.url,
        '{"jsonrpc":"2.0","method":"item.get","params":{"output":["type","interfaces"],"hostids":"' + hostid + '","selectInterfaces":"query"},"id":1}'
    )).result;
}

// Define an interface array. This is required if more than one ZBX interface exists on host level
var interfacesInUse = [];

// Iterate through all ZBX interfaces
for (var zbx = 0; zbx < allAgentInterfaces.length; zbx++) {

    // Go through all items which is defined at host level
    for (var int = 0; int < items_with_int.length; int++) {

        // There are many items which does not need interface. Specifically analyze the ones which has an interface defined
        if (items_with_int[int].interfaces.length > 0) {

            // There is an interface found for the item
            if (items_with_int[int].interfaces[0].interfaceid == allAgentInterfaces[zbx].interfaceid) {
                // Put this item in list which use an interface
                var row = {};
                row["itemid"] = items_with_int[int].itemid;
                row["interfaceid"] = allAgentInterfaces[zbx].interfaceid;
                row["main"] = allAgentInterfaces[zbx].main;
                interfacesInUse.push(row);
            }
        }
    }
}

// Final scan to identify if any interface is wasted
var needToDelete = 1;
var evidenceOfDeletedInterfaces = [];
var mainNotUsed = 0;
for (var defined = 0; defined < allAgentInterfaces.length; defined++) {

    // Scan all items
    needToDelete = 1;
    for (var used = 0; used < interfacesInUse.length; used++) {
        if (allAgentInterfaces[defined].interfaceid == interfacesInUse[used].interfaceid) {
            needToDelete = 0;
        }
    }

    // If flag was not turned off, then no items with this interface were found. No items are using this interface. Safe to delete
    // Delete all slaves first
    if (needToDelete == 1 && allAgentInterfaces[defined].main == 0) {
        var deleteInt = JSON.parse(request.post(params.url,
            '{"jsonrpc":"2.0","method":"hostinterface.delete","params":["' + allAgentInterfaces[defined].interfaceid + '"],"id":1}'
        ));
        var row = {};
        row["deleted"] = deleteInt;
        evidenceOfDeletedInterfaces.push(row);
    }

    if (needToDelete == 1 && allAgentInterfaces[defined].main == 1) {
        var mainNotUsed = allAgentInterfaces[defined].interfaceid;
    }

}

// Delete main interface at the end
if (mainNotUsed > 0) {
    var deleteInt = JSON.parse(request.post(params.url,
        '{"jsonrpc":"2.0","method":"hostinterface.delete","params":["' + mainNotUsed + '"],"id":1}'
    ));
    var row = {};
    row["deleted"] = deleteInt;
    evidenceOfDeletedInterfaces.push(row);
}

var output = JSON.stringify({
    "allAgentInterfaces": allAgentInterfaces,
    "interfacesInUse": interfacesInUse,
    "evidenceOfDeletedInterfaces": evidenceOfDeletedInterfaces
});

Zabbix.Log(params.debug, 'Auto remove unused ZBX agent passive interfaces: ' + output)

return 0;