Crea un plugin (tutorial)

Questo è un tutorial passo passo per creare un semplice plugin caricabile per l'agente Zabbix 2.

Cosa creerai

Durante questo tutorial, aggiungerai un nuovo plugin caricabile MyIP. Il plugin implementerà 1 metrica chiamata myip, che restituisce l'indirizzo IP esterno dell'host su cui è in esecuzione l'agente Zabbix 2.

Part 1: Writing the Go code

In this section you will learn how to write the plugin that adds a new metric to Zabbix agent 2.

  1. Create a new directory myip in /usr/local/zabbix/go/plugins/.

  2. Create the file main.go inside myip directory and define the name of your Go package.

Keep the file open to add more lines as described in the next steps.

/usr/local/zabbix/go/plugins/myip/main.go

package main
  1. Specify the packages to import. These are the packages that support the plugin.

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)
  1. Define the plugin structure.
    Embed the plugin.Base structure to gain access to the standard plugin functionality.

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)

type Plugin struct {
           plugin.Base
       }

       var impl Plugin
  1. Implement plugin interface Export. The export interface performs a poll and returns a value.

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)

type Plugin struct {
           plugin.Base
       }

       var impl Plugin

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error)
  1. Add logging.
    The log messages will appear in the Zabbix agent 2 log. You can use one of the logging functions available to plugins: Critf, Errf, Infof, Warningf, Debugf, Tracef.

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)

type Plugin struct {
           plugin.Base
       }

       var impl Plugin

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error){
    p.Infof("received request to handle %s key with %d parameters", key, len(params))
}
  1. Implement the core plugin logic. This logic fetches the response from the specified URL and reads it, then returns the IP address as a response and closes the request. In case of an error when executing the GET request or reading a response, the error is returned instead.

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)

type Plugin struct {
           plugin.Base
       }

       var impl Plugin

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error){
    p.Infof("received request to handle %s key with %d parameters", key, len(params))
    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. Register the metric. Zabbix agent 2 initiates running init() function upon startup. This function will call plugin.RegisterMetrics(structure, plugin name, metric name, description) method to get the plugin data.

The plugin.RegisterMetrics method parameter description:

  • structure - a pointer to plugin implementation; grants access to the plugin structure, including list of available plugin interfaces (for example, &impl).
  • name - plugin name; must be unique (for example, "Myip").
  • metric name - metric name (for example, "myip"). This is the item key used to gather data from a plugin.
  • description - metric description; must start with a capital letter and end with a period (for example, ""Return the external IP address of the host where agent is running.").

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

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)

type Plugin struct {
           plugin.Base
       }

       var impl Plugin

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error){
    p.Infof("received request to handle %s key with %d parameters", key, len(params))
    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    
}

func init() {
           plugin.RegisterMetrics(&impl, "Myip", "myip", "Return the external IP address of the host where agent is running.")
       }
  1. Define the main function, which will implement Zabbix plugin handler to create and start the plugin. Defining the main() function is mandatory.

/usr/local/zabbix/go/plugins/myip/main.go

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "git.zabbix.com/ap/plugin-support/plugin/container"
    "git.zabbix.com/ap/plugin-support/plugin"
)

type Plugin struct {
           plugin.Base
       }

       var impl Plugin

func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error){
    p.Infof("received request to handle %s key with %d parameters", key, len(params))
    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    
}

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

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()))
           }
       }

Part 2: building the plugin

  1. 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

You should receive an output 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
  1. Create an executable myip, which is the loadable plugin.

  2. Specify the path to the plugin configuration file in Zabbix agent 2 configuration file. The path should be set in the Plugins.<PluginName>.System.Path parameter. Replace <PluginName> with the name of the plugin, defined in the code when calling plugin.RegisterMetrics() function (for this tutorial, Plugins.Myip.System.Path).

echo 'Plugins.Myip.System.Path=/usr/local/zabbix/go/plugins/myip/myip' > /etc/zabbix_agent2.d/plugins.d/myip.conf
  1. Now, everything is ready to test the metric:
zabbix_agent2 -t myip

The response should contain the external IP address of your host.

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