Add Icinga2 input plugin (#4559)
This commit is contained in:
parent
a8496f87b2
commit
e72fab7cbe
|
@ -168,6 +168,7 @@ configuration options.
|
||||||
* [http_listener](./plugins/inputs/http_listener)
|
* [http_listener](./plugins/inputs/http_listener)
|
||||||
* [http](./plugins/inputs/http) (generic HTTP plugin, supports using input data formats)
|
* [http](./plugins/inputs/http) (generic HTTP plugin, supports using input data formats)
|
||||||
* [http_response](./plugins/inputs/http_response)
|
* [http_response](./plugins/inputs/http_response)
|
||||||
|
* [icinga2](./plugins/inputs/icinga2)
|
||||||
* [influxdb](./plugins/inputs/influxdb)
|
* [influxdb](./plugins/inputs/influxdb)
|
||||||
* [internal](./plugins/inputs/internal)
|
* [internal](./plugins/inputs/internal)
|
||||||
* [interrupts](./plugins/inputs/interrupts)
|
* [interrupts](./plugins/inputs/interrupts)
|
||||||
|
|
|
@ -42,6 +42,7 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/http_listener"
|
_ "github.com/influxdata/telegraf/plugins/inputs/http_listener"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/http_response"
|
_ "github.com/influxdata/telegraf/plugins/inputs/http_response"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/httpjson"
|
_ "github.com/influxdata/telegraf/plugins/inputs/httpjson"
|
||||||
|
_ "github.com/influxdata/telegraf/plugins/inputs/icinga2"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
|
_ "github.com/influxdata/telegraf/plugins/inputs/influxdb"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/internal"
|
_ "github.com/influxdata/telegraf/plugins/inputs/internal"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/interrupts"
|
_ "github.com/influxdata/telegraf/plugins/inputs/interrupts"
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Icinga2 Input Plugin
|
||||||
|
|
||||||
|
This plugin gather services & hosts status using Icinga2 Remote API.
|
||||||
|
|
||||||
|
The icinga2 plugin uses the icinga2 remote API to gather status on running
|
||||||
|
services and hosts. You can read Icinga2's documentation for their remote API
|
||||||
|
[here](https://docs.icinga.com/icinga2/latest/doc/module/icinga2/chapter/icinga2-api)
|
||||||
|
|
||||||
|
### Configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Description
|
||||||
|
[[inputs.icinga2]]
|
||||||
|
## Required Icinga2 server address (default: "https://localhost:5665")
|
||||||
|
# server = "https://localhost:5665"
|
||||||
|
|
||||||
|
## Required Icinga2 object type ("services" or "hosts, default "services")
|
||||||
|
# object_type = "services"
|
||||||
|
|
||||||
|
## Credentials for basic HTTP authentication
|
||||||
|
# username = "admin"
|
||||||
|
# password = "admin"
|
||||||
|
|
||||||
|
## Maximum time to receive response.
|
||||||
|
# response_timeout = "5s"
|
||||||
|
|
||||||
|
## Optional TLS Config
|
||||||
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
|
# tls_key = "/etc/telegraf/key.pem"
|
||||||
|
## Use TLS but skip chain & host verification
|
||||||
|
# insecure_skip_verify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Measurements & Fields:
|
||||||
|
|
||||||
|
- All measurements have the following fields:
|
||||||
|
- name (string)
|
||||||
|
- state_code (int)
|
||||||
|
|
||||||
|
### Tags:
|
||||||
|
|
||||||
|
- All measurements have the following tags:
|
||||||
|
- check_command
|
||||||
|
- display_name
|
||||||
|
- state
|
||||||
|
- source
|
||||||
|
- port
|
||||||
|
- scheme
|
||||||
|
|
||||||
|
### Sample Queries:
|
||||||
|
|
||||||
|
```
|
||||||
|
SELECT * FROM "icinga2_services" WHERE state_code = 0 AND time > now() - 24h // Service with OK status
|
||||||
|
SELECT * FROM "icinga2_services" WHERE state_code = 1 AND time > now() - 24h // Service with WARNING status
|
||||||
|
SELECT * FROM "icinga2_services" WHERE state_code = 2 AND time > now() - 24h // Service with CRITICAL status
|
||||||
|
SELECT * FROM "icinga2_services" WHERE state_code = 3 AND time > now() - 24h // Service with UNKNOWN status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./telegraf -config telegraf.conf -input-filter icinga2 -test
|
||||||
|
icinga2_hosts,display_name=router-fr.eqx.fr,check_command=hostalive-custom,host=test-vm,source=localhost,port=5665,scheme=https,state=ok name="router-fr.eqx.fr",state=0 1492021603000000000
|
||||||
|
```
|
|
@ -0,0 +1,170 @@
|
||||||
|
package icinga2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/internal"
|
||||||
|
"github.com/influxdata/telegraf/internal/tls"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Icinga2 struct {
|
||||||
|
Server string
|
||||||
|
ObjectType string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
ResponseTimeout internal.Duration
|
||||||
|
tls.ClientConfig
|
||||||
|
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
Results []Object `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Object struct {
|
||||||
|
Attrs Attribute `json:"attrs"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Joins struct{} `json:"joins"`
|
||||||
|
Meta struct{} `json:"meta"`
|
||||||
|
Type ObjectType `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attribute struct {
|
||||||
|
CheckCommand string `json:"check_command"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
State int `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var levels = []string{"ok", "warning", "critical", "unknown"}
|
||||||
|
|
||||||
|
type ObjectType string
|
||||||
|
|
||||||
|
var sampleConfig = `
|
||||||
|
## Required Icinga2 server address (default: "https://localhost:5665")
|
||||||
|
# server = "https://localhost:5665"
|
||||||
|
|
||||||
|
## Required Icinga2 object type ("services" or "hosts, default "services")
|
||||||
|
# object_type = "services"
|
||||||
|
|
||||||
|
## Credentials for basic HTTP authentication
|
||||||
|
# username = "admin"
|
||||||
|
# password = "admin"
|
||||||
|
|
||||||
|
## Maximum time to receive response.
|
||||||
|
# response_timeout = "5s"
|
||||||
|
|
||||||
|
## Optional TLS Config
|
||||||
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
|
# tls_key = "/etc/telegraf/key.pem"
|
||||||
|
## Use TLS but skip chain & host verification
|
||||||
|
# insecure_skip_verify = true
|
||||||
|
`
|
||||||
|
|
||||||
|
func (i *Icinga2) Description() string {
|
||||||
|
return "Gather Icinga2 status"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Icinga2) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Icinga2) GatherStatus(acc telegraf.Accumulator, checks []Object) {
|
||||||
|
for _, check := range checks {
|
||||||
|
fields := make(map[string]interface{})
|
||||||
|
tags := make(map[string]string)
|
||||||
|
|
||||||
|
url, err := url.Parse(i.Server)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields["name"] = check.Attrs.Name
|
||||||
|
fields["state_code"] = check.Attrs.State
|
||||||
|
|
||||||
|
tags["display_name"] = check.Attrs.DisplayName
|
||||||
|
tags["check_command"] = check.Attrs.CheckCommand
|
||||||
|
tags["state"] = levels[check.Attrs.State]
|
||||||
|
tags["source"] = url.Hostname()
|
||||||
|
tags["scheme"] = url.Scheme
|
||||||
|
tags["port"] = url.Port()
|
||||||
|
|
||||||
|
acc.AddFields(fmt.Sprintf("icinga2_%s", i.ObjectType), fields, tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Icinga2) createHttpClient() (*http.Client, error) {
|
||||||
|
tlsCfg, err := i.ClientConfig.TLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: tlsCfg,
|
||||||
|
},
|
||||||
|
Timeout: i.ResponseTimeout.Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Icinga2) Gather(acc telegraf.Accumulator) error {
|
||||||
|
if i.ResponseTimeout.Duration < time.Second {
|
||||||
|
i.ResponseTimeout.Duration = time.Second * 5
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.client == nil {
|
||||||
|
client, err := i.createHttpClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s/v1/objects/%s?attrs=name&attrs=display_name&attrs=state&attrs=check_command", i.Server, i.ObjectType)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if i.Username != "" {
|
||||||
|
req.SetBasicAuth(i.Username, i.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := i.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
result := Result{}
|
||||||
|
json.NewDecoder(resp.Body).Decode(&result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
i.GatherStatus(acc, result.Results)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inputs.Add("icinga2", func() telegraf.Input {
|
||||||
|
return &Icinga2{
|
||||||
|
Server: "https://localhost:5665",
|
||||||
|
ObjectType: "services",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package icinga2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGatherServicesStatus(t *testing.T) {
|
||||||
|
|
||||||
|
s := `{"results":[
|
||||||
|
{
|
||||||
|
"attrs": {
|
||||||
|
"check_command": "check-bgp-juniper-netconf",
|
||||||
|
"display_name": "eq-par.dc2.fr",
|
||||||
|
"name": "ef017af8-c684-4f3f-bb20-0dfe9fcd3dbe",
|
||||||
|
"state": 0
|
||||||
|
},
|
||||||
|
"joins": {},
|
||||||
|
"meta": {},
|
||||||
|
"name": "eq-par.dc2.fr!ef017af8-c684-4f3f-bb20-0dfe9fcd3dbe",
|
||||||
|
"type": "Service"
|
||||||
|
}
|
||||||
|
]}`
|
||||||
|
|
||||||
|
checks := Result{}
|
||||||
|
json.Unmarshal([]byte(s), &checks)
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"name": "ef017af8-c684-4f3f-bb20-0dfe9fcd3dbe",
|
||||||
|
"state_code": 0,
|
||||||
|
}
|
||||||
|
tags := map[string]string{
|
||||||
|
"display_name": "eq-par.dc2.fr",
|
||||||
|
"check_command": "check-bgp-juniper-netconf",
|
||||||
|
"state": "ok",
|
||||||
|
"source": "localhost",
|
||||||
|
"port": "5665",
|
||||||
|
"scheme": "https",
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
|
icinga2 := new(Icinga2)
|
||||||
|
icinga2.ObjectType = "services"
|
||||||
|
icinga2.Server = "https://localhost:5665"
|
||||||
|
icinga2.GatherStatus(&acc, checks.Results)
|
||||||
|
acc.AssertContainsTaggedFields(t, "icinga2_services", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatherHostsStatus(t *testing.T) {
|
||||||
|
|
||||||
|
s := `{"results":[
|
||||||
|
{
|
||||||
|
"attrs": {
|
||||||
|
"name": "webserver",
|
||||||
|
"address": "192.168.1.1",
|
||||||
|
"check_command": "ping",
|
||||||
|
"display_name": "apache",
|
||||||
|
"state": 2
|
||||||
|
},
|
||||||
|
"joins": {},
|
||||||
|
"meta": {},
|
||||||
|
"name": "webserver",
|
||||||
|
"type": "Host"
|
||||||
|
}
|
||||||
|
]}`
|
||||||
|
|
||||||
|
checks := Result{}
|
||||||
|
json.Unmarshal([]byte(s), &checks)
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"name": "webserver",
|
||||||
|
"state_code": 2,
|
||||||
|
}
|
||||||
|
tags := map[string]string{
|
||||||
|
"display_name": "apache",
|
||||||
|
"check_command": "ping",
|
||||||
|
"state": "critical",
|
||||||
|
"source": "localhost",
|
||||||
|
"port": "5665",
|
||||||
|
"scheme": "https",
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
|
icinga2 := new(Icinga2)
|
||||||
|
icinga2.ObjectType = "hosts"
|
||||||
|
icinga2.Server = "https://localhost:5665"
|
||||||
|
icinga2.GatherStatus(&acc, checks.Results)
|
||||||
|
acc.AssertContainsTaggedFields(t, "icinga2_hosts", fields, tags)
|
||||||
|
}
|
Loading…
Reference in New Issue