Add Icinga2 input plugin (#4559)

This commit is contained in:
LABOUARDY Mohamed 2018-08-23 03:10:40 +01:00 committed by Daniel Nelson
parent a8496f87b2
commit e72fab7cbe
5 changed files with 328 additions and 0 deletions

View File

@ -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)

View File

@ -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"

View File

@ -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
```

View File

@ -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",
}
})
}

View File

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