Node_Exporter/collector/dmi.go
Ben Kochie 1d5afd05b5
Sanitize UTF-8 in dmi collector (#2229)
Replace invalid UTF-8 chars with "�" string.

Fixes: https://github.com/prometheus/node_exporter/issues/2228

Signed-off-by: Ben Kochie <superq@gmail.com>
2021-12-01 11:13:43 +01:00

107 lines
3.4 KiB
Go
Raw Blame History

// Copyright 2021 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 linux && !nodmi
// +build linux,!nodmi
package collector
import (
"errors"
"fmt"
"os"
"strings"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs/sysfs"
)
type dmiCollector struct {
infoDesc *prometheus.Desc
values []string
}
func init() {
registerCollector("dmi", defaultEnabled, NewDMICollector)
}
// NewDMICollector returns a new Collector exposing DMI information.
func NewDMICollector(logger log.Logger) (Collector, error) {
fs, err := sysfs.NewFS(*sysPath)
if err != nil {
return nil, fmt.Errorf("failed to open sysfs: %w", err)
}
dmi, err := fs.DMIClass()
if err != nil {
if errors.Is(err, os.ErrNotExist) {
level.Debug(logger).Log("msg", "Platform does not support Desktop Management Interface (DMI) information", "err", err)
dmi = &sysfs.DMIClass{}
} else {
return nil, fmt.Errorf("failed to read Desktop Management Interface (DMI) information: %w", err)
}
}
var labels, values []string
for label, value := range map[string]*string{
"bios_date": dmi.BiosDate,
"bios_release": dmi.BiosRelease,
"bios_vendor": dmi.BiosVendor,
"bios_version": dmi.BiosVersion,
"board_asset_tag": dmi.BoardAssetTag,
"board_name": dmi.BoardName,
"board_serial": dmi.BoardSerial,
"board_vendor": dmi.BoardVendor,
"board_version": dmi.BoardVersion,
"chassis_asset_tag": dmi.ChassisAssetTag,
"chassis_serial": dmi.ChassisSerial,
"chassis_vendor": dmi.ChassisVendor,
"chassis_version": dmi.ChassisVersion,
"product_family": dmi.ProductFamily,
"product_name": dmi.ProductName,
"product_serial": dmi.ProductSerial,
"product_sku": dmi.ProductSKU,
"product_uuid": dmi.ProductUUID,
"product_version": dmi.ProductVersion,
"system_vendor": dmi.SystemVendor,
} {
if value != nil {
labels = append(labels, label)
values = append(values, strings.ToValidUTF8(*value, "<22>"))
}
}
// Construct DMI metric only once since it will not change until the next reboot.
return &dmiCollector{
infoDesc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "dmi", "info"),
"A metric with a constant '1' value labeled by bios_date, bios_release, bios_vendor, bios_version, "+
"board_asset_tag, board_name, board_serial, board_vendor, board_version, chassis_asset_tag, "+
"chassis_serial, chassis_vendor, chassis_version, product_family, product_name, product_serial, "+
"product_sku, product_uuid, product_version, system_vendor if provided by DMI.",
labels, nil,
),
values: values,
}, nil
}
func (c *dmiCollector) Update(ch chan<- prometheus.Metric) error {
if len(c.values) == 0 {
return ErrNoData
}
ch <- prometheus.MustNewConstMetric(c.infoDesc, prometheus.GaugeValue, 1.0, c.values...)
return nil
}