Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ See the `-help` output for more options.
Include CIB data
-collector.checker
Include CheckerComponent data
-collector.objects
Include Object data
-debug
Enable debug logging
-icinga.api string
Expand Down Expand Up @@ -52,6 +54,7 @@ The tables below list all existing collectors.
| APIListener | `-collector.apilistener` |
| CIB | `-collector.cib` |
| CheckerComponent | `-collector.checker` |
| Objects | `-collector.objects` |

# Development

Expand Down
6 changes: 6 additions & 0 deletions icinga2_exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func main() {
cliCollectorApiListener bool
cliCollectorCIB bool
cliCollectorChecker bool
cliCollectorObjects bool
)

flag.StringVar(&cliListenAddress, "web.listen-address", ":9665", "Address on which to expose metrics and web interface.")
Expand All @@ -76,6 +77,7 @@ func main() {
flag.BoolVar(&cliCollectorApiListener, "collector.apilistener", false, "Include APIListener data")
flag.BoolVar(&cliCollectorCIB, "collector.cib", false, "Include CIB data")
flag.BoolVar(&cliCollectorChecker, "collector.checker", false, "Include CheckerComponent data")
flag.BoolVar(&cliCollectorObjects, "collector.objects", false, "Include Object data")

flag.BoolVar(&cliVersion, "version", false, "Print version")
flag.BoolVar(&cliDebugLog, "debug", false, "Enable debug logging")
Expand Down Expand Up @@ -140,6 +142,10 @@ func main() {
prometheus.MustRegister(collector.NewIcinga2CheckerCollector(c, logger))
}

if cliCollectorObjects {
prometheus.MustRegister(collector.NewIcinga2ObjectsCollector(c, logger))
}

// Create a central context to propagate a shutdown
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancel()
Expand Down
76 changes: 76 additions & 0 deletions internal/collector/objects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package collector

import (
"log/slog"

"github.com/martialblog/icinga2-exporter/internal/icinga"

"github.com/prometheus/client_golang/prometheus"
)

const (
zonesObject = "zones"
usersObject = "users"
)

type Icinga2ObjectsCollector struct {
icingaClient *icinga.Client
logger *slog.Logger
objects_zones_num *prometheus.Desc
objects_users_num *prometheus.Desc
}

func NewIcinga2ObjectsCollector(client *icinga.Client, logger *slog.Logger) *Icinga2ObjectsCollector {
return &Icinga2ObjectsCollector{
icingaClient: client,
logger: logger,
objects_zones_num: prometheus.NewDesc("icinga2_objects_zones_num", "Number of Zone objects", nil, nil),
objects_users_num: prometheus.NewDesc("icinga2_objects_users_num", "Number of User objects", nil, nil),
}
}

func (collector *Icinga2ObjectsCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- collector.objects_zones_num
ch <- collector.objects_users_num
}

func (collector *Icinga2ObjectsCollector) Collect(ch chan<- prometheus.Metric) {
collector.collectZones(ch)
collector.collectUsers(ch)
}

func (collector *Icinga2ObjectsCollector) collectZones(ch chan<- prometheus.Metric) {
result, err := collector.icingaClient.GetObjects(zonesObject)

if err != nil {
collector.logger.Error("Could not retrieve Zones metrics", "error", err.Error())
return
}

if len(result.Results) < 1 {
collector.logger.Debug("No results for Zones metrics")
return
}

zoneCount := float64(len(result.Results))

ch <- prometheus.MustNewConstMetric(collector.objects_zones_num, prometheus.GaugeValue, zoneCount)
}

func (collector *Icinga2ObjectsCollector) collectUsers(ch chan<- prometheus.Metric) {
result, err := collector.icingaClient.GetObjects(usersObject)

if err != nil {
collector.logger.Error("Could not retrieve Users metrics", "error", err.Error())
return
}

if len(result.Results) < 1 {
collector.logger.Debug("No results for Users metrics")
return
}

userCount := float64(len(result.Results))

ch <- prometheus.MustNewConstMetric(collector.objects_users_num, prometheus.GaugeValue, userCount)
}
19 changes: 19 additions & 0 deletions internal/icinga/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
)

const (
endpointObjects = "/objects"
endpointApiListener = "/status/ApiListener"
endpointApplication = "/status/IcingaApplication"
endpointCIB = "/status/CIB"
Expand Down Expand Up @@ -167,6 +168,24 @@ func (icinga *Client) GetCheckerComponentMetrics() (CheckerComponentResult, erro
return result, nil
}

func (icinga *Client) GetObjects(object string) (ObjectsResult, error) {
var result ObjectsResult

body, errBody := icinga.fetchJSON(endpointObjects + "/" + object)

if errBody != nil {
return result, fmt.Errorf("error fetching response: %w", errBody)
}

errDecode := json.Unmarshal(body, &result)

if errDecode != nil {
return result, fmt.Errorf("error parsing response: %w", errDecode)
}

return result, nil
}

func (icinga *Client) fetchJSON(endpoint string) ([]byte, error) {
// Lookup data in the cache we go out and bother the Icinga API
if elem, ok := icinga.cache.Get(endpoint); ok {
Expand Down
7 changes: 7 additions & 0 deletions internal/icinga/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,10 @@ type CheckerComponentResult struct {
Perfdata []Perfdata `json:"perfdata,omitempty"`
} `json:"results"`
}

type ObjectsResult struct {
Results []struct {
Name string `json:"name"`
Type string `json:"type"`
} `json:"results"`
}
14 changes: 14 additions & 0 deletions internal/icinga/testdata/objects1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
curl -X GET -s -k -u 'root:0e7ff601de4a5426' 'https://localhost:5665/v1/objects/users?pretty=1' -d '{ "attrs": [ "__name"]}'
{
"results": [
{
"attrs": {
"__name": "icingaadmin"
},
"joins": {},
"meta": {},
"name": "icingaadmin",
"type": "User"
}
]
}