Documentation

Table of Contents

Loadable plugins

A loadable plugin for Zabbix agent 2 is a stand-alone application that the agent can work with. Zabbix sources and compilation of the agent are not required. The only requirement is to build the plugin in a desired way. The communication of Zabbix agent 2 with a loadable plugin and metrics collection process is depicted in the Connection Diagram.

The entire plugin building process includes three stages. During the first stage Go code is installed, then plugin support packages and the environment for the installation is prepared, and finally the actual loadable plugin building instructions.
See the instructions given below.

Stage 1

  1. In order to create a loadable plugin that functions in the same way as the built-in one, the Go code can be placed in:
`/usr/local/zabbix/go/plugins/myip` directory.
  1. Create the file main.go:
mkdir -p /usr/local/zabbix/go/plugins/myip
       cd /usr/local/zabbix/go/plugins/myip
       vi main.go

The structure of the code is almost the same as that of the built-in plugin. The only exception is the necessity to define a main() function. Now, a stand-alone application is created, and the package is named "main" and the list of imports is a bit different, too.

  1. Add the code to the file. This is the source code of a loadable plugin Myip. It implements one metric called myip, which returns the external IP address of the host where the agent is running. Refer to Stage 2 for further details below:
// This is the source code of plugin Myip. It implements 1 metric
       // called myip, which returns the external IP address of the host where
       // Zabbix agent 2 is running.
       
       package main
       
       // Packages we will use.
       
       import (
           "fmt"
           "io/ioutil"
           "net/http"
           "git.zabbix.com/ap/plugin-support/plugin/container"
           "git.zabbix.com/ap/plugin-support/plugin"
       )
       
       // Plugin must define structure and embed plugin.Base structure.
       type Plugin struct {
           plugin.Base
       }
       
       // Create a new instance of the defined plugin structure
       var impl Plugin
       
       // Plugin must implement one or several plugin interfaces.
       func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
           // You may use one of Critf, Errf, Infof, Warningf, Debugf, Tracef functions for logging.
           p.Infof("received request to handle %s key with %d parameters", key, len(params))
       
           // Fetch response from the specified URL, it should be just the IP address.
           resp, err := http.Get("https://api.ipify.org")
           if err != nil {
               // Plugin will return an error response if the request failed
               return nil, err
           }
       
           defer resp.Body.Close()
       
           body, err := ioutil.ReadAll(resp.Body)
           if err != nil {
           // Plugin will return an error response if it failed to read the response
               return nil, err
           }
       
           return string(body), nil
       }
       
       func init() {
           // Register our metric, specifying the plugin and metric details.
           // 1 - a pointer to plugin implementation
           // 2 - plugin name
           // 3 - metric name (item key)
           // 4 - metric description
           //
           // NB! The metric description must end with a period, otherwise the Zabbix agent 2 will return an error and won't start!
           // Metric name (item key) and metric description can be repeated in a loop to register additional metrics.
           plugin.RegisterMetrics(&impl, "Myip", "myip", "Return the external IP address of the host where agent is running.")
       }
       
       // This is the main function, it is required to compile the plugin.
       // By default the function implements our packages to handle the plugin creation and execution.
       func main() {
           h, err := container.NewHandler(impl.Name())
           if err != nil {
               panic(fmt.Sprintf("failed to create plugin handler %s", err.Error()))
           }
           impl.Logger = &h
       
           err = h.Execute()
           if err != nil {
               panic(fmt.Sprintf("failed to execute plugin handler %s", err.Error()))
           }
       }

Stage 2

  1. Package definition. This is the name of your Go package. All the files in the package must have the same name. It implements one metric, called myip, which returns the external IP address of the host where Zabbix agent is running.
package main
  1. Package import. These are the packages that support the plugin. Following packages are used:
import (
           "fmt"
           "io/ioutil"
           "net/http"
           "git.zabbix.com/ap/plugin-support/plugin/container"
           "git.zabbix.com/ap/plugin-support/plugin"
       )
  1. Plugin structure. A plugin must define the structure and embed the plugin.Base structure. This grants the access to all the functionality of a plugin.
type Plugin struct {
           plugin.Base
       }
       
       var impl Plugin
  1. Plugin Export interface. A plugin must implement one or several plugin interfaces. This is where all the plugin functionality is handled. Currently, the Export interface is implemented.
func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
  1. Logging. Use one of Critf, Errf, Infof, Warningf, Debugf, Tracef logging functions available to plugins, in case you need them. These log messages will appear in the same place where the agent is logging.
p.Infof("received request to handle %s key with %d parameters", key, len(params))
  1. Core logic. Fetches the response from the specified URL and reads it, then returns the IP address as a response and closes the response. In case of an error when executing the GET request or when reading the response, the error is returned instead.
    resp, err := http.Get("https://api.ipify.org")
           if err != nil {
               return nil, err
           }
       
           defer resp.Body.Close()
       
           body, err := ioutil.ReadAll(resp.Body)
           if err != nil {
               return nil, err
           }
       
           return string(body), nil
       }
  1. Registering the metric. Init function is run when Zabbix agent 2 is started. It provides the plugin name, metrics, and interfaces it implements. The first parameter grants access to the plugin structure, which provides the information on all the available plugin interfaces.
    The second parameter defines the plugin name, which must be unique and not clash with any other already existing plugin name.
    The third parameter defines the metric name. This is the key used to gather data from a plugin. the fourth parameter is the description, which must start with a capital letter and end with a period.

The third and fourth parameters can be input multiple times in a row to register multiple metrics.

func init() {
           plugin.RegisterMetrics(&impl, "Myip", "myip", "Return the external IP address of the host where agent is running.")
       }

The metric description must end with a period, otherwise the agent will not start!

  1. This is the main function, which implements Zabbix plugin handler to create and start the plugin.
func main() {
           h, err := container.NewHandler(impl.Name())
           if err != nil {
               panic(fmt.Sprintf("failed to create plugin handler %s", err.Error()))
           }
           impl.Logger = &h
       
           err = h.Execute()
           if err != nil {
               panic(fmt.Sprintf("failed to execute plugin handler %s", err.Error()))
           }
       }

Stage 3

  1. Building the plugin. Execute this bash script to create go files for dependency handling, and to download the dependencies automatically:
go mod init myip
       GOPROXY=direct go mod tidy
       go build

The output will be something like this:

go: creating new go.mod: module myip
       go: to add module requirements and sums:
           go mod tidy
       go: finding module for package git.zabbix.com/ap/plugin-support/plugin/container
       go: finding module for package git.zabbix.com/ap/plugin-support/plugin
       go: found git.zabbix.com/ap/plugin-support/plugin in git.zabbix.com/ap/plugin-support v0.0.0-20220608100211-35b8bffd7ad0
       go: found git.zabbix.com/ap/plugin-support/plugin/container in git.zabbix.com/ap/plugin-support v0.0.0-20220608100211-35b8bffd7ad0
  1. Then, an executable myip should be created, which is the loadable plugin. It is necessary to tell Zabbix agent 2 where to find it. (Ensure that the /etc/zabbix_agent2.d/plugins.d path is correct):
echo 'Plugins.Myip.System.Path=/usr/local/zabbix/go/plugins/myip/myip' > /etc/zabbix_agent2.d/plugins.d/myip.conf

Ensure that the path is set correctly, using Myip. This is the name of the plugin, which was defined in the code when calling plugin.RegisterMetrics() function.

  1. Now, everything is ready to test the metric:
zabbix_agent2 -t myip

As a result, the external IP address of your host should be displayed.

In case of any errors, first make sure that the user zabbix has permissions to access /usr/local/zabbix/go/plugins/myip directory.

More information on loadable plugins

Loadable Zabbix agent 2 plugins are supported since Zabbix 6.0.0. These plugins are kept in a separate repository, however, they use a shared with Zabbix agent 2 package.

Loadable plugins need to be compiled and built separately. Plugin configuration shall be provided in a separate Zabbix agent 2 configuration file in the same way as it is provided for built-in plugins.

Loadable plugins support the following interfaces:

  • Exporter (except the ContextProvider parameter);
  • Runner;
  • Configurator.

Watcher and Collector interfaces are not supported.

Zabbix agent 2 connects bidirectionally to the plugins using UNIX sockets on Linux and Named Pipes on Windows.

Several example loadable plugins are available in Zabbix source code and can be used as a reference.

Documentation

Last but not least step of creating a good plugin, is to create a Readme file containing the following information:

  • A short description of the plugin;
  • Plugin configuration parameters and steps required for configuring; connection with the system being monitored;
  • A list of supported keys;
  • Known limitations/problems (if any).