A loadable plugin for Zabbix agent 2 is a standalone 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 between Zabbix agent 2 and a loadable plugin as well as the metrics collection process are depicted in the connection diagram below.
The entire plugin building process includes three stages. First, Go code is installed, then plugin support packages and the environment for the installation are prepared, and finally the actual loadable plugin building is performed.
This tutorial provides step-by-step instructions for each stage.
Feel free to use our example repository as a template or guide for creating your own loadable plugins.
main.go
:The code structure is almost the same as that of a built-in plugin. The only exception is the necessity to define a main() function. Thus, a standalone application is created, the package is named "main", and the list of imports might differ.
// 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 to import.
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 fails
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// Plugin will return an error response if it fails to read the response
return nil, err
}
return string(body), nil
}
func init() {
// Register the 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 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, and it is required to compile the plugin.
// The function creates a new plugin handler instance, assigns it to be used for logging by the plugin and then executes the plugin handler.
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()))
}
}
import (
"fmt"
"io/ioutil"
"net/http"
"git.zabbix.com/ap/plugin-support/plugin/container"
"git.zabbix.com/ap/plugin-support/plugin"
)
func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) {
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
}
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!
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()))
}
}
go mod init myip
GOPROXY=direct go get git.zabbix.com/ap/plugin-support/plugin@branchname
go mod tidy
go build
Make sure to specify the correct branch name, i.e. replace branchname
(see Line 2) with one of the following:
release/*
- for the stable release branch, where "*" is the release version (i.e. 6.0
)master
- for the master branch<commit hash>
- for the specific commit version (use the specific commit hash)The output should be similar to 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
/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.
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.
Loadable Zabbix agent 2 plugins are supported since Zabbix 6.0.0. These plugins are kept in a separate repository, however, they use a package shared with Zabbix agent 2.
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:
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.
A final step of creating a good plugin is adding a Readme file with the following information: