Files
windows_exporter/internal/collector/mscluster/mscluster_node.go
T

234 lines
8.3 KiB
Go

// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
package mscluster
import (
"time"
"github.com/prometheus-community/windows_exporter/internal/headers/clusapi"
"github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const nameNode = Name + "_node"
type nodeSource interface {
Nodes(deadline time.Time) ([]clusapi.Object, error)
Close() error
}
type collectorNode struct {
nodeSource nodeSource
// nodeMetricCache reuses the field metrics of unchanged nodes; see
// objectMetricCache.
nodeMetricCache objectMetricCache
nodeBuildNumber *prometheus.Desc
nodeCharacteristics *prometheus.Desc
nodeDetectedCloudPlatform *prometheus.Desc
nodeDynamicWeight *prometheus.Desc
nodeFlags *prometheus.Desc
nodeMajorVersion *prometheus.Desc
nodeMinorVersion *prometheus.Desc
nodeNeedsPreventQuorum *prometheus.Desc
nodeNodeDrainStatus *prometheus.Desc
nodeNodeHighestVersion *prometheus.Desc
nodeNodeLowestVersion *prometheus.Desc
nodeNodeWeight *prometheus.Desc
nodeState *prometheus.Desc
nodeStatusInformation *prometheus.Desc
}
// msClusterNode represents the MSCluster_Node WMI class. The collector reads
// ClusAPI; the parity test compares against this WMI model.
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-node
type msClusterNode struct {
Name string `mi:"Name"`
BuildNumber uint `mi:"BuildNumber"`
Characteristics uint `mi:"Characteristics"`
DetectedCloudPlatform uint `mi:"DetectedCloudPlatform"`
DynamicWeight uint `mi:"DynamicWeight"`
Flags uint `mi:"Flags"`
MajorVersion uint `mi:"MajorVersion"`
MinorVersion uint `mi:"MinorVersion"`
NeedsPreventQuorum uint `mi:"NeedsPreventQuorum"`
NodeDrainStatus uint `mi:"NodeDrainStatus"`
NodeHighestVersion uint `mi:"NodeHighestVersion"`
NodeLowestVersion uint `mi:"NodeLowestVersion"`
NodeWeight uint `mi:"NodeWeight"`
State uint `mi:"State"`
StatusInformation uint `mi:"StatusInformation"`
}
func (c *Collector) buildNode() error {
source, err := clusapi.Open()
if err != nil {
return err
}
c.nodeSource = source
c.buildNodeDescriptors()
return nil
}
func (c *Collector) buildNodeDescriptors() {
c.nodeBuildNumber = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "build_number"),
"Provides access to the node's BuildNumber property.",
[]string{"name"},
nil,
)
c.nodeCharacteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "characteristics"),
"Provides access to the characteristics set for the node.",
[]string{"name"},
nil,
)
c.nodeDetectedCloudPlatform = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "detected_cloud_platform"),
"(DetectedCloudPlatform)",
[]string{"name"},
nil,
)
c.nodeDynamicWeight = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "dynamic_weight"),
"The dynamic vote weight of the node adjusted by dynamic quorum feature.",
[]string{"name"},
nil,
)
c.nodeFlags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "flags"),
"Provides access to the flags set for the node.",
[]string{"name"},
nil,
)
c.nodeMajorVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "major_version"),
"Provides access to the node's MajorVersion property, which specifies the major portion of the Windows version installed.",
[]string{"name"},
nil,
)
c.nodeMinorVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "minor_version"),
"Provides access to the node's MinorVersion property, which specifies the minor portion of the Windows version installed.",
[]string{"name"},
nil,
)
c.nodeNeedsPreventQuorum = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "needs_prevent_quorum"),
"Whether the cluster service on that node should be started with prevent quorum flag.",
[]string{"name"},
nil,
)
c.nodeNodeDrainStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_drain_status"),
"The current node drain status of a node. 0: Not Initiated; 1: In Progress; 2: Completed; 3: Failed",
[]string{"name"},
nil,
)
c.nodeNodeHighestVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_highest_version"),
"Provides access to the node's NodeHighestVersion property, which specifies the highest possible version of the cluster service with which the node can join or communicate.",
[]string{"name"},
nil,
)
c.nodeNodeLowestVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_lowest_version"),
"Provides access to the node's NodeLowestVersion property, which specifies the lowest possible version of the cluster service with which the node can join or communicate.",
[]string{"name"},
nil,
)
c.nodeNodeWeight = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_weight"),
"The vote weight of the node.",
[]string{"name"},
nil,
)
c.nodeState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "state"),
"Returns the current state of a node. -1: Unknown; 0: Up; 1: Down; 2: Paused; 3: Joining",
[]string{"name"},
nil,
)
c.nodeStatusInformation = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "status_information"),
"The isolation or quarantine status of the node.",
[]string{"name"},
nil,
)
// Cached metrics refer to the descriptors of an earlier build.
c.nodeMetricCache.Reset()
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel. It returns the node names that
// the resource and resource group subcollectors use for owner_node.
func (c *Collector) collectNode(ch chan<- prometheus.Metric, maxScrapeDuration time.Duration) ([]string, error) {
if c.nodeSource == nil {
return nil, errNotBuilt(subCollectorNode)
}
nodes, resultErr := callSource(maxScrapeDuration, c.nodeSource.Nodes)
return c.publishNodes(ch, nodes, osversion.Build(), resultErr)
}
func (c *Collector) publishNodes(ch chan<- prometheus.Metric, nodes []clusapi.Object, build uint16, resultErr error) ([]string, error) {
fields := []objectField{
{name: "BuildNumber", desc: c.nodeBuildNumber},
{name: "Characteristics", desc: c.nodeCharacteristics},
// The previous WMI query selected DetectedCloudPlatform only on Windows
// Server 2022 and newer and published 0 on older builds.
{name: "DetectedCloudPlatform", desc: c.nodeDetectedCloudPlatform, minBuild: osversion.LTSC2022, older: zeroIfMissing},
{name: "DynamicWeight", desc: c.nodeDynamicWeight},
{name: "Flags", desc: c.nodeFlags},
{name: "MajorVersion", desc: c.nodeMajorVersion},
{name: "MinorVersion", desc: c.nodeMinorVersion},
{name: "NeedsPreventQuorum", desc: c.nodeNeedsPreventQuorum},
{name: "NodeDrainStatus", desc: c.nodeNodeDrainStatus},
{name: "NodeHighestVersion", desc: c.nodeNodeHighestVersion},
{name: "NodeLowestVersion", desc: c.nodeNodeLowestVersion},
{name: "NodeWeight", desc: c.nodeNodeWeight},
{name: "State", desc: c.nodeState},
{name: "StatusInformation", desc: c.nodeStatusInformation, minBuild: osversion.LTSC2016},
}
nodeNames := make([]string, 0, len(nodes))
scrape := c.nodeMetricCache.Begin()
var key []byte
for _, node := range nodes {
if node.Name == "" {
continue
}
key, resultErr = publishCachedObjectFields(ch, scrape, key, "node", node, fields, build, resultErr)
nodeNames = append(nodeNames, node.Name)
}
commitScrape(scrape, len(nodes), resultErr)
return nodeNames, resultErr
}