[comment]: # ({f238de62-e03ef403})
# Izveidot spraudni (pamācība)

Šī ir soli pa solim pamācība par to, kā izveidot vienkāršu ielādējamu spraudni Zabbix aģentam 2.

Varat arī izmantot [piemēra repozitoriju](https://git.zabbix.com/projects/AP/repos/example/browse) kā veidni vai ceļvedi savu spraudņu izveidei.

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

[comment]: # ({4aa59460-3b9b2b36})
### Ko jūs izveidosiet

Šajā pamācībā ir parādīts, kā izveidot jaunu ielādējamu spraudni **MyIP**.
Spraudnis ieviesīs vienu vienuma atslēgu **myip**, kas atgriezīs tā hosta ārējo IP adresi, kurā darbojas Zabbix aģents 2.

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

[comment]: # ({bf3f1fff-47915903})
### 1. darbība: iestatīšana


1. Spraudnis ir standarta Go modulis.
Sāciet ar `go.mod` faila inicializēšanu spraudņa direktorijā, lai sekotu spraudņa atkarībām:

```sh
cd path/to/plugins/myip # Pārslēdzieties uz savu spraudņa direktoriju
go mod init myip
```

2. Instalējiet obligāto atkarību Zabbix Go SDK (`golang.zabbix.com/sdk`):

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

Aizstājiet `$LATEST_COMMIT_HASH` ar jaunāko `HEAD` commit hash no `golang.zabbix.com/sdk` [krātuves](https://git.zabbix.com/projects/AP/repos/plugin-support/commits) attiecīgajā laidiena zarā.
Piemēram:

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

Ņemiet vērā, ka `golang.zabbix.com/sdk` versiju pārvaldība pašlaik netiek atbalstīta, taču nākotnē tas var mainīties.

Papildu atkarības pēc vajadzības var instalēt, izmantojot `go get`.

3. Izveidojiet tukšu `main.go` failu spraudņa pirmkoda vajadzībām:

```sh
touch main.go
```

Tagad sākotnējā iestatīšana ir pabeigta, un spraudnis ir gatavs izstrādei.

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

[comment]: # ({f8ebbdf8-a76d6ea7})
### 2. darbība: spraudņa struktūra

`golang.zabbix.com/sdk` modulis, kas tika instalēts iepriekšējā darbībā, nodrošina nepieciešamo ietvaru spraudņu izstrādei un garantē, ka visiem spraudņiem ir vienota struktūra.

1. Iestatiet pamata izpildes plūsmu.

Sāciet ar spraudņa galvenās izpildes plūsmas definēšanu. Pievienojiet šādu kodu failam `main.go`:

```go
package main

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

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

Tas izveido spraudņa pamata izpildes plūsmu.
Funkcija `run` vēlāk saturēs spraudņa pamatloģiku.

2. Iepazīstieties ar spraudņa interfeisiem.

Zabbix aģents 2 spraudnim jābūt attēlotam ar struktūru, kas īsteno interfeisus no `golang.zabbix.com/sdk/plugin` pakotnes:

- *Accessor* - definē būtiskās metodes, kas jāīsteno visiem spraudņiem, piemēram, spraudņa nosaukuma iestatīšanu un vienuma atslēgas taimautu apstrādi.
- Vienu vai vairākus no šiem funkcionālajiem spraudņa interfeisiem:
	- *Exporter* - veic aptauju un atgriež vērtību (vai vērtības), neko vai kļūdu; bieži tiek izmantots kopā ar *Collector* interfeisu.
	- *Collector* - pārvalda periodisku datu vākšanu.
	- *Runner* - definē spraudņa palaišanas un apturēšanas procedūras.
	- *Watcher* - ļauj īstenot neatkarīgu metriku aptauju, apejot aģenta iekšējo plānotāju; noderīgs uz trigeriem balstītai vai notikumu vadītai uzraudzībai.
	- *Configurator* - definē, kā spraudnis nolasa un piemēro savus konfigurācijas iestatījumus.

Jūs varat šos interfeisus īstenot paši vai izmantot Zabbix Go SDK nodrošinātos noklusējuma variantus, pielāgojot tos pēc vajadzības. 
Šajā pamācībā tiek izmantotas noklusējuma implementācijas.

3. Izveidojiet spraudņa struktūru.

Tagad importējiet *plugin* pakotni un izveidojiet `myIP` struktūru, kas iegulda `plugin.Base` struktūru:

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

type myIP struct {
	plugin.Base
}
```

Pašlaik `myIP` struktūra atbilst `Accessor` interfeisam. 
Metode viena no funkcionālajiem spraudņa interfeisiem, `Exporter`, īstenošanai tiks pievienota vēlāk pamācībā.

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

[comment]: # ({c3726cdb-4c521593})
### 3. darbība: Definējiet vienumu atslēgas

Jūsu spraudnim ir nepieciešamas vienumu atslēgas, lai savāktu datus un nodotu tos Zabbix serverim vai starpniekserverim.

1. Importējiet *errs* pakotni kļūdu apstrādei:

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

2. Reģistrējiet vienumu atslēgas, izmantojot funkciju `plugin.RegisterMetrics()` funkcijā `run()`:

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

	// Reģistrē `myip` vienumu atslēgu.
	err := plugin.RegisterMetrics(
		p,
		"MyIP",                           // Spraudņa nosaukums
		"myip",                           // Vienumu atslēgas nosaukums
		"Atgriež hosts IP adresi.",        // Vienumu atslēgas apraksts
	)
	if err != nil {
		return errs.Wrap(err, "neizdevās reģistrēt metriku")
	}

	return nil
}
```

Lai reģistrētu vairākas vienumu atslēgas, atkārtojiet parametrus *metric name* un *description* katrai metrikai. 
Piemēram:

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

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

[comment]: # ({8227eb99-b7f436a7})
### 4. darbība: iestatiet apdarinātāju

Apdarinātājs nodrošina saziņu starp aģentu un spraudni.

1. Importējiet pakotni *container*:

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

2. Funkcijā `run()` pievienojiet kodu, lai izveidotu un iestatītu apdarinātāju:

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

	// Reģistrējiet `myip` vienuma atslēgu.
	err := plugin.RegisterMetrics(
		p,
		"MyIP",                           // Spraudņa nosaukums
		"myip",                           // Vienuma atslēgas nosaukums
		"Atgriež hosts IP adresi.",        // Vienuma atslēgas apraksts
	)
	if err != nil {
		return errs.Wrap(err, "failed to register metrics")
	}

	// Izveidojiet jaunu apdarinātāju.
	h, err := container.NewHandler("MyIP") // Spraudņa nosaukums
	if err != nil {
		return errs.Wrap(err, "failed to create new handler")
	}

	// Iestatiet žurnālu pārsūtīšanu no spraudņa uz aģentu.
	// Pieejams, izmantojot p.Logger.Infof, p.Logger.Debugf utt.
	p.Logger = h

	// Sāciet spraudņa izpildi.
	// Bloķējas līdz no aģenta tiek saņemts pārtraukšanas pieprasījums.
	err = h.Execute()
	if err != nil {
		return errs.Wrap(err, "failed to execute plugin handler")
	}

	return nil
}
```

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

[comment]: # ({d6f0f4ab-c7541684})
### 5. darbība: datu vākšanas ieviešana

Datu vākšana tiek veikta, izmantojot `Exporter` interfeisu, kas apraksta `Export` metodi:

```go
func Export(
  key string,             // Vienuma atslēga, no kuras vākt datus.
  params []string,        // Argumenti, kas nodoti vienuma atslēgai (`myip[arg1, arg2]`).
  context ContextProvider // Metadati par vienuma atslēgas datu vākšanu.
) (any, error)
```

1. Importējiet nepieciešamās pakotnes HTTP pieprasījumiem un atbildes nolasīšanai:

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

2. Ieviesiet `Export` metodi struktūrai `myIP`:

```go
func (p *myIP) Export(
	key string, params []string, context plugin.ContextProvider,
) (any, error) {
	// Spraudnis var izmantot atšķirīgu datu vākšanas loģiku atkarībā no `key` parametra.
    // Šī ieviešana tikai pārbauda, vai norādītais `key` tiek atbalstīts.
	if key != "myip" {
		return nil, errs.Errorf("unknown item key %q", key)
	}

	// Žurnāla ieraksts tiks pārsūtīts uz aģents 2 žurnālu.
	p.Infof(
		"received request to handle %q key with %d parameters",
		key,
		len(params),
	)

	// Savāciet datus un atgrieziet tos.

	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})
### 6. darbība: spraudņa izveide un konfigurēšana

1. Lai izveidotu spraudni, izpildiet:

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

Tam vajadzētu izveidot izpildāmo failu `myip` pašreizējā direktorijā.

2. Konfigurējiet Zabbix aģents 2, lai izmantotu spraudni:

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

Aizstājiet `$PATH_TO_THE_MYIP_PLUGIN_EXECUTABLE` ar ceļu uz `myip`, kas tika izveidots 5. darbībā.

Spraudņa nosaukumam konfigurācijas parametra nosaukumā (_MyIP_ šajā pamācībā) ir jāsakrīt ar spraudņa nosaukumu, kas definēts funkcijā _plugin.RegisterMetrics()_.

3. Lai pārbaudītu spraudni un tā `myip` vienumu, izpildiet:

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

Izvadē jābūt jūsu hosta ārējai IP adresei, un tai vajadzētu izskatīties līdzīgi šim:

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

Tādējādi jūs esat izveidojis vienkāršu ielādējamu spraudni Zabbix aģents 2.

Apsveicam!

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

[comment]: # ({6aef1ccf-38f17bf9})
### Pilns avota kods

```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{}

	// Reģistrē `myip` vienuma atslēgu.
	err := plugin.RegisterMetrics(
		p,
		"MyIP",                           // Spraudņa nosaukums
		"myip",                           // Vienuma atslēgas nosaukums
		"Atgriež hosts IP adresi.",        // Vienuma atslēgas apraksts
	)
	if err != nil {
		return errs.Wrap(err, "neizdevās reģistrēt metriku")
	}

	// Izveido jaunu apstrādātāju.
	h, err := container.NewHandler("MyIP") // Spraudņa nosaukums
	if err != nil {
		return errs.Wrap(err, "neizdevās izveidot jaunu apstrādātāju")
	}

	// Iestata reģistrēšanu, lai pārsūtītu žurnālus no spraudņa uz aģentu.
	// Pieejams, izmantojot p.Logger.Infof, p.Logger.Debugf u. c.
	p.Logger = h

	// Sāk spraudņa izpildi.
	// Bloķējas, līdz no aģenta tiek saņemts pārtraukšanas pieprasījums.
	err = h.Execute()
	if err != nil {
		return errs.Wrap(err, "neizdevās izpildīt spraudņa apstrādātāju")
	}

	return nil
}

func (p *myIP) Export(
	key string, params []string, context plugin.ContextProvider,
) (any, error) {
	// Spraudnis var izmantot atšķirīgu datu vākšanas loģiku atkarībā no `key` parametra.  
    // Šī implementācija tikai pārbauda, vai norādītais `key` tiek atbalstīts. 
	if key != "myip" {
		return nil, errs.Errorf("nezināma vienuma atslēga %q", key)
	}

	// Žurnāls tiks pārsūtīts uz aģenta 2 žurnālu.
	p.Infof(
		"saņemts pieprasījums apstrādāt %q atslēgu ar %d parametriem",
		key,
		len(params),
	)

	// Savāc datus un atgriež tos.

	resp, err := http.Get("https://api.ipify.org")
	if err != nil {
		return nil, errs.Wrap(err, "neizdevās iegūt IP adresi")
	}

	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, errs.Wrap(err, "neizdevās nolasīt atbildes pamattekstu")
	}

	return string(body), nil
}
```

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

