forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Icinga2 input plugin (influxdata#4559)
- Loading branch information
1 parent
06f8661
commit 2572f2b
Showing
5 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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", | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |