[comment]: # ({f238de62-e03ef403})
# Creare un plugin (tutorial)

Questo è un tutorial passo passo su come creare un semplice plugin caricabile per Zabbix agent 2.

Puoi anche utilizzare il [repository di esempio](https://git.zabbix.com/projects/AP/repos/example/browse) come modello o guida per creare i tuoi plugin.

[comment]: # ({/f238de62-e03ef403})

[comment]: # ({4aa59460-3b9b2b36})
### Cosa creerai

Questo tutorial mostra come creare un nuovo plugin caricabile **MyIP**.
Il plugin implementerà una singola chiave di elemento, **myip**, che restituisce l'indirizzo IP esterno dell'host su cui è in esecuzione Zabbix agent 2.

[comment]: # ({/4aa59460-3b9b2b36})

[comment]: # ({bf3f1fff-47915903})
### Passaggio 1: Setup

1. Un plugin è un modulo Go standard.
Inizia inizializzando il file `go.mod` nella directory del plugin per tracciarne le dipendenze:

```sh
cd path/to/plugins/myip # Passa alla directory del tuo plugin
go mod init myip
```

2. Installa la dipendenza obbligatoria Zabbix Go SDK (`golang.zabbix.com/sdk`):

```sh
go get golang.zabbix.com/sdk@$LATEST_COMMIT_HASH
```

Sostituisci `$LATEST_COMMIT_HASH` con il più recente hash del commit `HEAD` dal [repository](https://git.zabbix.com/projects/AP/repos/plugin-support/commits) `golang.zabbix.com/sdk` nel branch di rilascio appropriato.
Ad esempio:

```sh
go get golang.zabbix.com/sdk@af85407
```

Si noti che il versioning di `golang.zabbix.com/sdk` non è attualmente supportato, ma questo potrebbe cambiare in futuro.

È possibile installare dipendenze aggiuntive, se necessario, utilizzando `go get`.

3. Crea un file `main.go` vuoto per il codice sorgente del plugin:

```sh
touch main.go
```

Ora il setup iniziale è completo e il plugin è pronto per lo sviluppo.

[comment]: # ({/bf3f1fff-47915903})

[comment]: # ({f8ebbdf8-a76d6ea7})
### Passo 2: Struttura del plugin

Il modulo `golang.zabbix.com/sdk`, installato nel passaggio precedente, fornisce il framework necessario per lo sviluppo dei plugin e garantisce che tutti i plugin abbiano una struttura coerente.

1. Configurare il flusso di esecuzione di base.

Inizia definendo il flusso di esecuzione principale del plugin. Aggiungi il seguente codice a `main.go`:

```go
package main

func main() {
	err := run()
	if err != nil {
		panic(err)
	}
}

func run() error {
	return nil
}
```

Questo stabilisce il flusso di esecuzione di base per il plugin.
La funzione run conterrà in seguito la logica principale del plugin.

2. Esplorare le interfacce del plugin.

Un plugin di Zabbix agent 2 deve essere rappresentato da una struct che implementa le interfacce del pacchetto `golang.zabbix.com/sdk/plugin`:

- *Accessor* - definisce i metodi essenziali che tutti i plugin devono implementare, come l'impostazione del nome del plugin e la gestione dei timeout della chiave dell'item.
- Una o più delle seguenti interfacce funzionali del plugin:
	- *Exporter* - esegue un poll e restituisce un valore (o valori), nulla oppure un errore; spesso usato insieme all'interfaccia *Collector*.
	- *Collector* - gestisce la raccolta periodica dei dati.
	- *Runner* - definisce le procedure di avvio e arresto del plugin.
	- *Watcher* - consente di implementare il polling indipendente delle metriche, bypassando lo scheduler interno dell'agent; utile per il monitoraggio basato su trap o guidato da eventi.
	- *Configurator* - definisce come il plugin legge e applica le proprie impostazioni di configurazione.

Puoi implementare queste interfacce autonomamente oppure usare quelle predefinite fornite dallo Zabbix Go SDK, modificandole secondo necessità. 
Questo tutorial utilizza le implementazioni predefinite.

3. Creare la struct del plugin.

Ora importa il pacchetto *plugin* e crea una struct `myIP` che incorpora la struct `plugin.Base`:

```go
import "golang.zabbix.com/sdk/plugin"

type myIP struct {
	plugin.Base
}
```

La struct myIP soddisfa attualmente l'interfaccia Accessor. 
Più avanti nel tutorial verrà aggiunto un metodo per implementare una delle interfacce funzionali del plugin, l'`Exporter`.

[comment]: # ({/f8ebbdf8-a76d6ea7})

[comment]: # ({c3726cdb-4c521593})
### Passaggio 3: Definire le chiavi item

Il tuo plugin ha bisogno delle chiavi item per raccogliere i dati e fornirli a Zabbix server o proxy.

1. Importa il pacchetto *errs* per la gestione degli errori:

```go
import "golang.zabbix.com/sdk/errs"
```

2. Registra le chiavi item usando la funzione `plugin.RegisterMetrics()` all'interno della funzione `run()`:

```go
func run() error {
	p := &myIP{}

	// Registra la chiave item `myip`.
	err := plugin.RegisterMetrics(
		p,
		"MyIP",                           // Nome del plugin
		"myip",                           // Nome della chiave item
		"Restituisce l'indirizzo IP dell'host.", // Descrizione della chiave item
	)
	if err != nil {
		return errs.Wrap(err, "failed to register metrics")
	}

	return nil
}
```

Per registrare più chiavi item, ripeti i parametri *metric name* e *description* per ciascuna metrica. 
Ad esempio:

```go
plugin.RegisterMetrics(&impl, "Myip", "metric.one", "Metric one description.", "metric.two", "Metric two description.")
```

[comment]: # ({/c3726cdb-4c521593})

[comment]: # ({8227eb99-b7f436a7})
### Passaggio 4: Configurare l'handler

L'handler facilita la comunicazione tra l'agent e il plugin.

1. Importa il package *container*:

```go
import "golang.zabbix.com/sdk/plugin/container"
```

2. All'interno della funzione `run()` aggiungi il codice per creare e configurare un handler:

```go
func run() error {
	p := &myIP{}

	// Registra la chiave item `myip`.
	err := plugin.RegisterMetrics(
		p,
		"MyIP",                           // Nome del plugin
		"myip",                           // Nome della chiave item
		"Restituisce l'indirizzo IP dell'host.", // Descrizione della chiave item
	)
	if err != nil {
		return errs.Wrap(err, "failed to register metrics")
	}

	// Crea un nuovo handler.
	h, err := container.NewHandler("MyIP") // Nome del plugin
	if err != nil {
		return errs.Wrap(err, "failed to create new handler")
	}

	// Configura il logging per inoltrare i log dal plugin all'agent.
	// Disponibile tramite p.Logger.Infof, p.Logger.Debugf, ecc.
	p.Logger = h

	// Avvia l'esecuzione del plugin.
	// Rimane in attesa fino a quando non viene ricevuta una richiesta di terminazione dall'agent.
	err = h.Execute()
	if err != nil {
		return errs.Wrap(err, "failed to execute plugin handler")
	}

	return nil
}
```

[comment]: # ({/8227eb99-b7f436a7})

[comment]: # ({d6f0f4ab-c7541684})
### Passaggio 5: Implementare la raccolta dati

La raccolta dati viene eseguita tramite l'interfaccia Exporter, che descrive il metodo `Export`:

```go
func Export(
  key string,             // La chiave dell'item da raccogliere.
  params []string,        // Argomenti passati alla chiave dell'item (`myip[arg1, arg2]`).
  context ContextProvider // Metadati sulla raccolta dati della chiave dell'item.
) (any, error)
```

1. Importa i pacchetti necessari per le richieste HTTP e la lettura della risposta:

```
import (
	"io"
	"net/http"
)
```

2. Implementa il metodo `Export` per la struct `myIP`:

```go
func (p *myIP) Export(
	key string, params []string, context plugin.ContextProvider,
) (any, error) {
	// Il plugin può usare una logica di raccolta dati diversa in base al parametro `key`.
    // Questa implementazione verifica solo che il `key` fornito sia supportato.
	if key != "myip" {
		return nil, errs.Errorf("unknown item key %q", key)
	}

	// Il log verrà inoltrato al log di agent 2.
	p.Infof(
		"received request to handle %q key with %d parameters",
		key,
		len(params),
	)

	// Raccogli i dati e restituiscili.

	resp, err := http.Get("https://api.ipify.org")
	if err != nil {
		return nil, errs.Wrap(err, "failed to get IP address")
	}

	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, errs.Wrap(err, "failed to read response body")
	}

	return string(body), nil
}
```

[comment]: # ({/d6f0f4ab-c7541684})

[comment]: # ({c7bcafc2-f9325698})
### Passo 6: Compilare e configurare il plugin

1. Per compilare il plugin, esegui:

```bash
go mod tidy
go build
```

Questo dovrebbe creare un eseguibile `myip` nella directory corrente.

2. Configura Zabbix agent 2 per usare il plugin:

```sh
echo "Plugins.MyIP.System.Path=$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE" > /etc/zabbix_agent2.d/plugins.d/myip.conf
```

Sostituisci `$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE` con il percorso del `myip` creato nel passaggio 5.

Il nome del plugin nel nome del parametro di configurazione (_MyIP_ in questo tutorial) deve corrispondere al nome del plugin definito nella funzione _plugin.RegisterMetrics()_.

3. Per testare il plugin e il relativo item `myip`, esegui:

```sh
zabbix_agent2 -c /etc/zabbix_agent2.conf -t myip
```

L'output dovrebbe contenere un indirizzo IP esterno del tuo host e apparire simile a questo:

```
myip                                          [s|192.0.2.1]
```

Con questo, hai creato un semplice plugin caricabile per Zabbix agent 2.
Complimenti!

[comment]: # ({/c7bcafc2-f9325698})

[comment]: # ({6aef1ccf-38f17bf9})
### Codice sorgente completo

```go
package main

import (
	"io"
	"net/http"

	"golang.zabbix.com/sdk/errs"
	"golang.zabbix.com/sdk/plugin"
	"golang.zabbix.com/sdk/plugin/container"
)

var _ plugin.Exporter = (*myIP)(nil)

type myIP struct {
	plugin.Base
}

func main() {
	err := run()
	if err != nil {
		panic(err)
	}
}

func run() error {
	p := &myIP{}

	// Registra la chiave item `myip`.
	err := plugin.RegisterMetrics(
		p,
		"MyIP",                           // Nome del plugin
		"myip",                           // Nome della chiave item
		"Restituisce l'indirizzo IP dell'host.", // Descrizione della chiave item
	)
	if err != nil {
		return errs.Wrap(err, "impossibile registrare le metriche")
	}

	// Crea un nuovo handler.
	h, err := container.NewHandler("MyIP") // Nome del plugin
	if err != nil {
		return errs.Wrap(err, "impossibile creare un nuovo handler")
	}

	// Configura la registrazione per inoltrare i log dal plugin all'agent.
	// Disponibile tramite p.Logger.Infof, p.Logger.Debugf, ecc.
	p.Logger = h

	// Avvia l'esecuzione del plugin.
	// Rimane bloccato finché non viene ricevuta una richiesta di terminazione dall'agent.
	err = h.Execute()
	if err != nil {
		return errs.Wrap(err, "impossibile eseguire l'handler del plugin")
	}

	return nil
}

func (p *myIP) Export(
	key string, params []string, context plugin.ContextProvider,
) (any, error) {
	// Il plugin può usare una logica diversa di raccolta dati in base al parametro `key`.
    // Questa implementazione verifica solo che il `key` fornito sia supportato.
	if key != "myip" {
		return nil, errs.Errorf("chiave item sconosciuta %q", key)
	}

	// Il log verrà inoltrato al log dell'agent 2.
	p.Infof(
		"ricevuta richiesta per gestire la chiave %q con %d parametri",
		key,
		len(params),
	)

	// Raccoglie i dati e li restituisce.

	resp, err := http.Get("https://api.ipify.org")
	if err != nil {
		return nil, errs.Wrap(err, "impossibile ottenere l'indirizzo IP")
	}

	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, errs.Wrap(err, "impossibile leggere il corpo della risposta")
	}

	return string(body), nil
}
```

[comment]: # ({/6aef1ccf-38f17bf9})

