From 40dce45d8dfd5f89abc00eb94a4a252d755e0552 Mon Sep 17 00:00:00 2001
From: Paul Gier <pgier@redhat.com>
Date: Tue, 29 Jan 2019 16:54:47 -0600
Subject: [PATCH] collector/systemd: add new label "type" for
 systemd_unit_state (#1229)

Adds a new label called "type" systemd_unit_state which contains the
Type field from the unit file.  This applies only to the .service and
.mount unit types.  The other unit types do not include the optional
type field.

Fixes #1210

Signed-off-by: Paul Gier <pgier@redhat.com>
---
 collector/systemd_linux.go | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/collector/systemd_linux.go b/collector/systemd_linux.go
index ac1d0186..b34c938c 100644
--- a/collector/systemd_linux.go
+++ b/collector/systemd_linux.go
@@ -17,14 +17,14 @@ package collector
 
 import (
 	"fmt"
+	"math"
 	"regexp"
 	"strings"
 
 	"github.com/coreos/go-systemd/dbus"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/common/log"
-	"gopkg.in/alecthomas/kingpin.v2"
-	"math"
+	kingpin "gopkg.in/alecthomas/kingpin.v2"
 )
 
 var (
@@ -61,7 +61,7 @@ func NewSystemdCollector() (Collector, error) {
 
 	unitDesc := prometheus.NewDesc(
 		prometheus.BuildFQName(namespace, subsystem, "unit_state"),
-		"Systemd unit", []string{"name", "state"}, nil,
+		"Systemd unit", []string{"name", "state", "type"}, nil,
 	)
 	unitStartTimeDesc := prometheus.NewDesc(
 		prometheus.BuildFQName(namespace, subsystem, "unit_start_time_seconds"),
@@ -153,7 +153,7 @@ func (c *systemdCollector) collectUnitStatusMetrics(ch chan<- prometheus.Metric,
 			}
 			ch <- prometheus.MustNewConstMetric(
 				c.unitDesc, prometheus.GaugeValue, isActive,
-				unit.Name, stateName)
+				unit.Name, stateName, unit.serviceType)
 		}
 		if strings.HasSuffix(unit.Name, ".service") && unit.nRestarts != nil {
 			ch <- prometheus.MustNewConstMetric(
@@ -252,11 +252,22 @@ type unit struct {
 	tasksCurrent        *uint64
 	tasksMax            *uint64
 	nRestarts           *uint32
+	serviceType         string
 	acceptedConnections uint32
 	currentConnections  uint32
 	refusedConnections  *uint32
 }
 
+// unitType gets the suffix after the last "." in the
+// unit name and capitalizes the first letter
+func (u *unit) unitType() string {
+	suffixIndex := strings.LastIndex(u.Name, ".") + 1
+	if suffixIndex < 1 || suffixIndex > len(u.Name) {
+		return ""
+	}
+	return strings.Title(u.Name[suffixIndex:])
+}
+
 func (c *systemdCollector) getAllUnits() ([]unit, error) {
 	conn, err := c.newDbus()
 	if err != nil {
@@ -276,7 +287,15 @@ func (c *systemdCollector) getAllUnits() ([]unit, error) {
 		unit := unit{
 			UnitStatus: status,
 		}
-
+		unitType := unit.unitType()
+		if unitType == "Service" || unitType == "Mount" {
+			serviceType, err := conn.GetUnitTypeProperty(unit.Name, unitType, "Type")
+			if err != nil {
+				log.Debugf("couldn't get type for unit '%s': %s", unit.Name, err)
+			} else {
+				unit.serviceType = serviceType.Value.Value().(string)
+			}
+		}
 		if strings.HasSuffix(unit.Name, ".timer") {
 			lastTriggerValue, err := conn.GetUnitTypeProperty(unit.Name, "Timer", "LastTriggerUSec")
 			if err != nil {