Skip to content

Commit

Permalink
license collector (#1524)
Browse files Browse the repository at this point in the history
  • Loading branch information
jkroepke authored Jul 23, 2024
1 parent 64c8423 commit fa8d28c
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Name | Description | Enabled by default
[fsrmquota](docs/collector.fsrmquota.md) | Microsoft File Server Resource Manager (FSRM) Quotas collector |
[hyperv](docs/collector.hyperv.md) | Hyper-V hosts |
[iis](docs/collector.iis.md) | IIS sites and applications |
[license](docs/collector.license.md) | Windows license status |
[logical_disk](docs/collector.logical_disk.md) | Logical disks, disk I/O | ✓
[logon](docs/collector.logon.md) | User logon sessions |
[memory](docs/collector.memory.md) | Memory usage metrics |
Expand Down
53 changes: 53 additions & 0 deletions docs/collector.license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# license collector

The license collector exposes metrics about the Windows license status.

|||
-|-
Metric name prefix | `license`
Data source | Win32
Enabled by default? | No

## Flags

None

## Metrics

| Name | Description | Type | Labels |
|--------------------------|----------------|-------|---------|
| `windows_license_status` | license status | gauge | `state` |

### Example metric

```
# HELP windows_license_status Status of windows license
# TYPE windows_license_status gauge
windows_license_status{state="genuine"} 1
windows_license_status{state="invalid_license"} 0
windows_license_status{state="last"} 0
windows_license_status{state="offline"} 0
windows_license_status{state="tampered"} 0
```


## Useful queries

Show if the license is genuine

```
windows_license_status{state="genuine"}
```

## Alerting examples
**prometheus.rules**
```yaml
- alert: "WindowsLicense"
expr: 'windows_license_status{state="genuine"} == 0'
for: "10m"
labels:
severity: "high"
annotations:
summary: "Windows system license is not genuine"
description: "The Windows system license is not genuine. Please check the license status."
```
3 changes: 3 additions & 0 deletions pkg/collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"

"github.com/prometheus-community/windows_exporter/pkg/collector/ad"
"github.com/prometheus-community/windows_exporter/pkg/collector/adcs"
"github.com/prometheus-community/windows_exporter/pkg/collector/adfs"
Expand All @@ -23,6 +24,7 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
"github.com/prometheus-community/windows_exporter/pkg/collector/license"
"github.com/prometheus-community/windows_exporter/pkg/collector/logical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/logon"
"github.com/prometheus-community/windows_exporter/pkg/collector/memory"
Expand Down Expand Up @@ -103,6 +105,7 @@ func NewWithConfig(logger log.Logger, config Config) Collectors {
collectors[exchange.Name] = exchange.New(logger, &config.Fsrmquota)
collectors[hyperv.Name] = hyperv.New(logger, &config.Hyperv)
collectors[iis.Name] = iis.New(logger, &config.Iis)
collectors[license.Name] = license.New(logger, &config.License)
collectors[logical_disk.Name] = logical_disk.New(logger, &config.LogicalDisk)
collectors[logon.Name] = logon.New(logger, &config.Logon)
collectors[memory.Name] = memory.New(logger, &config.Memory)
Expand Down
3 changes: 3 additions & 0 deletions pkg/collector/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/exchange"
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
"github.com/prometheus-community/windows_exporter/pkg/collector/license"
"github.com/prometheus-community/windows_exporter/pkg/collector/logical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/logon"
"github.com/prometheus-community/windows_exporter/pkg/collector/memory"
Expand Down Expand Up @@ -74,6 +75,7 @@ type Config struct {
Fsrmquota exchange.Config `yaml:"fsrmquota"`
Hyperv hyperv.Config `yaml:"hyperv"`
Iis iis.Config `yaml:"iis"`
License license.Config `yaml:"license"`
LogicalDisk logical_disk.Config `yaml:"logical_disk"`
Logon logon.Config `yaml:"logon"`
Memory memory.Config `yaml:"memory"`
Expand Down Expand Up @@ -133,6 +135,7 @@ var ConfigDefaults = Config{
Fsrmquota: exchange.ConfigDefaults,
Hyperv: hyperv.ConfigDefaults,
Iis: iis.ConfigDefaults,
License: license.ConfigDefaults,
LogicalDisk: logical_disk.ConfigDefaults,
Logon: logon.ConfigDefaults,
Memory: memory.ConfigDefaults,
Expand Down
95 changes: 95 additions & 0 deletions pkg/collector/license/license.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//go:build windows

package license

import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"

"github.com/prometheus-community/windows_exporter/pkg/headers/slc"
"github.com/prometheus-community/windows_exporter/pkg/types"
)

const Name = "license"

var labelMap = map[slc.SL_GENUINE_STATE]string{
slc.SL_GEN_STATE_IS_GENUINE: "genuine",
slc.SL_GEN_STATE_INVALID_LICENSE: "invalid_license",
slc.SL_GEN_STATE_TAMPERED: "tampered",
slc.SL_GEN_STATE_OFFLINE: "offline",
slc.SL_GEN_STATE_LAST: "last",
}

type Config struct{}

var ConfigDefaults = Config{}

// A collector is a Prometheus collector for WMI Win32_PerfRawData_DNS_DNS metrics
type collector struct {
logger log.Logger

LicenseStatus *prometheus.Desc
}

func New(logger log.Logger, _ *Config) types.Collector {
c := &collector{}
c.SetLogger(logger)
return c
}

func NewWithFlags(_ *kingpin.Application) types.Collector {
return &collector{}
}

func (c *collector) GetName() string {
return Name
}

func (c *collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}

func (c *collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}

func (c *collector) Build() error {
c.LicenseStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "status"),
"Status of windows license",
[]string{"state"},
nil,
)

return nil
}

// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting license metrics", "err", err)
return err
}
return nil
}

func (c *collector) collect(ch chan<- prometheus.Metric) error {
status, err := slc.SLIsWindowsGenuineLocal()
if err != nil {
return err
}

for k, v := range labelMap {
val := 0.0
if status == k {
val = 1.0
}

ch <- prometheus.MustNewConstMetric(c.LicenseStatus, prometheus.GaugeValue, val, v)
}

return nil
}
12 changes: 12 additions & 0 deletions pkg/collector/license/license_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package license_test

import (
"testing"

"github.com/prometheus-community/windows_exporter/pkg/collector/license"
"github.com/prometheus-community/windows_exporter/pkg/testutils"
)

func BenchmarkCollector(b *testing.B) {
testutils.FuncBenchmarkCollector(b, license.Name, license.NewWithFlags)
}
2 changes: 2 additions & 0 deletions pkg/collector/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/fsrmquota"
"github.com/prometheus-community/windows_exporter/pkg/collector/hyperv"
"github.com/prometheus-community/windows_exporter/pkg/collector/iis"
"github.com/prometheus-community/windows_exporter/pkg/collector/license"
"github.com/prometheus-community/windows_exporter/pkg/collector/logical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/logon"
"github.com/prometheus-community/windows_exporter/pkg/collector/memory"
Expand Down Expand Up @@ -78,6 +79,7 @@ var Map = map[string]types.CollectorBuilderWithFlags{
fsrmquota.Name: fsrmquota.NewWithFlags,
hyperv.Name: hyperv.NewWithFlags,
iis.Name: iis.NewWithFlags,
license.Name: license.NewWithFlags,
logical_disk.Name: logical_disk.NewWithFlags,
logon.Name: logon.NewWithFlags,
memory.Name: memory.NewWithFlags,
Expand Down
40 changes: 40 additions & 0 deletions pkg/headers/slc/slc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package slc

import (
"errors"
"unsafe"

"golang.org/x/sys/windows"
)

var (
slc = windows.NewLazySystemDLL("slc.dll")
procSLIsWindowsGenuineLocal = slc.NewProc("SLIsWindowsGenuineLocal")
)

// Define SL_GENUINE_STATE enumeration
// https://learn.microsoft.com/en-us/windows/win32/api/slpublic/ne-slpublic-sl_genuine_state
type SL_GENUINE_STATE uint32

const (
SL_GEN_STATE_IS_GENUINE SL_GENUINE_STATE = iota
SL_GEN_STATE_INVALID_LICENSE
SL_GEN_STATE_TAMPERED
SL_GEN_STATE_OFFLINE
SL_GEN_STATE_LAST
)

// SLIsWindowsGenuineLocal function wrapper
func SLIsWindowsGenuineLocal() (SL_GENUINE_STATE, error) {
var genuineState SL_GENUINE_STATE

_, _, err := procSLIsWindowsGenuineLocal.Call(
uintptr(unsafe.Pointer(&genuineState)),
)

if !errors.Is(err, windows.NTE_OP_OK) {
return 0, err
}

return genuineState, nil
}

0 comments on commit fa8d28c

Please sign in to comment.