mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-11-23 20:36:21 +01:00
darwin powersupply collector (#1777)
* Extract powersupply linux code from collector common file. * Add Darwin powersupply collector. Signed-off-by: Alessio Caiazza <nolith@abisso.org>
This commit is contained in:
parent
ee17ba0fc0
commit
6523fdfc4b
@ -11,21 +11,17 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build !nopowersupplyclass && linux
|
//go:build !nopowersupplyclass && (linux || darwin)
|
||||||
// +build !nopowersupplyclass,linux
|
// +build !nopowersupplyclass
|
||||||
|
// +build linux darwin
|
||||||
|
|
||||||
package collector
|
package collector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-kit/log"
|
"github.com/go-kit/log"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/procfs/sysfs"
|
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,151 +49,3 @@ func NewPowerSupplyClassCollector(logger log.Logger) (Collector, error) {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error {
|
|
||||||
powerSupplyClass, err := getPowerSupplyClassInfo(c.ignoredPattern)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
|
||||||
return ErrNoData
|
|
||||||
}
|
|
||||||
return fmt.Errorf("could not get power_supply class info: %w", err)
|
|
||||||
}
|
|
||||||
for _, powerSupply := range powerSupplyClass {
|
|
||||||
|
|
||||||
for name, value := range map[string]*int64{
|
|
||||||
"authentic": powerSupply.Authentic,
|
|
||||||
"calibrate": powerSupply.Calibrate,
|
|
||||||
"capacity": powerSupply.Capacity,
|
|
||||||
"capacity_alert_max": powerSupply.CapacityAlertMax,
|
|
||||||
"capacity_alert_min": powerSupply.CapacityAlertMin,
|
|
||||||
"cyclecount": powerSupply.CycleCount,
|
|
||||||
"online": powerSupply.Online,
|
|
||||||
"present": powerSupply.Present,
|
|
||||||
"time_to_empty_seconds": powerSupply.TimeToEmptyNow,
|
|
||||||
"time_to_full_seconds": powerSupply.TimeToFullNow,
|
|
||||||
} {
|
|
||||||
if value != nil {
|
|
||||||
pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value), powerSupply.Name, prometheus.GaugeValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, value := range map[string]*int64{
|
|
||||||
"current_boot": powerSupply.CurrentBoot,
|
|
||||||
"current_max": powerSupply.CurrentMax,
|
|
||||||
"current_ampere": powerSupply.CurrentNow,
|
|
||||||
"energy_empty": powerSupply.EnergyEmpty,
|
|
||||||
"energy_empty_design": powerSupply.EnergyEmptyDesign,
|
|
||||||
"energy_full": powerSupply.EnergyFull,
|
|
||||||
"energy_full_design": powerSupply.EnergyFullDesign,
|
|
||||||
"energy_watthour": powerSupply.EnergyNow,
|
|
||||||
"voltage_boot": powerSupply.VoltageBoot,
|
|
||||||
"voltage_max": powerSupply.VoltageMax,
|
|
||||||
"voltage_max_design": powerSupply.VoltageMaxDesign,
|
|
||||||
"voltage_min": powerSupply.VoltageMin,
|
|
||||||
"voltage_min_design": powerSupply.VoltageMinDesign,
|
|
||||||
"voltage_volt": powerSupply.VoltageNow,
|
|
||||||
"voltage_ocv": powerSupply.VoltageOCV,
|
|
||||||
"charge_control_limit": powerSupply.ChargeControlLimit,
|
|
||||||
"charge_control_limit_max": powerSupply.ChargeControlLimitMax,
|
|
||||||
"charge_counter": powerSupply.ChargeCounter,
|
|
||||||
"charge_empty": powerSupply.ChargeEmpty,
|
|
||||||
"charge_empty_design": powerSupply.ChargeEmptyDesign,
|
|
||||||
"charge_full": powerSupply.ChargeFull,
|
|
||||||
"charge_full_design": powerSupply.ChargeFullDesign,
|
|
||||||
"charge_ampere": powerSupply.ChargeNow,
|
|
||||||
"charge_term_current": powerSupply.ChargeTermCurrent,
|
|
||||||
"constant_charge_current": powerSupply.ConstantChargeCurrent,
|
|
||||||
"constant_charge_current_max": powerSupply.ConstantChargeCurrentMax,
|
|
||||||
"constant_charge_voltage": powerSupply.ConstantChargeVoltage,
|
|
||||||
"constant_charge_voltage_max": powerSupply.ConstantChargeVoltageMax,
|
|
||||||
"precharge_current": powerSupply.PrechargeCurrent,
|
|
||||||
"input_current_limit": powerSupply.InputCurrentLimit,
|
|
||||||
"power_watt": powerSupply.PowerNow,
|
|
||||||
} {
|
|
||||||
if value != nil {
|
|
||||||
pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/1e6, powerSupply.Name, prometheus.GaugeValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, value := range map[string]*int64{
|
|
||||||
"temp_celsius": powerSupply.Temp,
|
|
||||||
"temp_alert_max_celsius": powerSupply.TempAlertMax,
|
|
||||||
"temp_alert_min_celsius": powerSupply.TempAlertMin,
|
|
||||||
"temp_ambient_celsius": powerSupply.TempAmbient,
|
|
||||||
"temp_ambient_max_celsius": powerSupply.TempAmbientMax,
|
|
||||||
"temp_ambient_min_celsius": powerSupply.TempAmbientMin,
|
|
||||||
"temp_max_celsius": powerSupply.TempMax,
|
|
||||||
"temp_min_celsius": powerSupply.TempMin,
|
|
||||||
} {
|
|
||||||
if value != nil {
|
|
||||||
pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/10.0, powerSupply.Name, prometheus.GaugeValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
keys []string
|
|
||||||
values []string
|
|
||||||
)
|
|
||||||
for name, value := range map[string]string{
|
|
||||||
"power_supply": powerSupply.Name,
|
|
||||||
"capacity_level": powerSupply.CapacityLevel,
|
|
||||||
"charge_type": powerSupply.ChargeType,
|
|
||||||
"health": powerSupply.Health,
|
|
||||||
"manufacturer": powerSupply.Manufacturer,
|
|
||||||
"model_name": powerSupply.ModelName,
|
|
||||||
"serial_number": powerSupply.SerialNumber,
|
|
||||||
"status": powerSupply.Status,
|
|
||||||
"technology": powerSupply.Technology,
|
|
||||||
"type": powerSupply.Type,
|
|
||||||
"usb_type": powerSupply.UsbType,
|
|
||||||
"scope": powerSupply.Scope,
|
|
||||||
} {
|
|
||||||
if value != "" {
|
|
||||||
keys = append(keys, name)
|
|
||||||
values = append(values, strings.ToValidUTF8(value, "<22>"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldDesc := prometheus.NewDesc(
|
|
||||||
prometheus.BuildFQName(namespace, c.subsystem, "info"),
|
|
||||||
"info of /sys/class/power_supply/<power_supply>.",
|
|
||||||
keys,
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func pushPowerSupplyMetric(ch chan<- prometheus.Metric, subsystem string, name string, value float64, powerSupplyName string, valueType prometheus.ValueType) {
|
|
||||||
fieldDesc := prometheus.NewDesc(
|
|
||||||
prometheus.BuildFQName(namespace, subsystem, name),
|
|
||||||
fmt.Sprintf("%s value of /sys/class/power_supply/<power_supply>.", name),
|
|
||||||
[]string{"power_supply"},
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(fieldDesc, valueType, value, powerSupplyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPowerSupplyClassInfo(ignore *regexp.Regexp) (sysfs.PowerSupplyClass, error) {
|
|
||||||
fs, err := sysfs.NewFS(*sysPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
powerSupplyClass, err := fs.PowerSupplyClass()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return powerSupplyClass, fmt.Errorf("error obtaining power_supply class info: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for device := range powerSupplyClass {
|
|
||||||
if ignore.MatchString(device) {
|
|
||||||
delete(powerSupplyClass, device)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return powerSupplyClass, nil
|
|
||||||
}
|
|
||||||
|
418
collector/powersupplyclass_darwin.go
Normal file
418
collector/powersupplyclass_darwin.go
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
// Copyright 2019 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 !nopowersupplyclass
|
||||||
|
// +build !nopowersupplyclass
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo LDFLAGS: -framework IOKit -framework CoreFoundation
|
||||||
|
#include <IOKit/ps/IOPowerSources.h>
|
||||||
|
#include <IOKit/ps/IOPSKeys.h>
|
||||||
|
#include <CoreFoundation/CFArray.h>
|
||||||
|
|
||||||
|
// values collected from IOKit Power Source APIs
|
||||||
|
// Functions documentation available at
|
||||||
|
// https://developer.apple.com/documentation/iokit/iopowersources_h
|
||||||
|
// CFDictionary keys definition
|
||||||
|
// https://developer.apple.com/documentation/iokit/iopskeys_h/defines
|
||||||
|
struct macos_powersupply {
|
||||||
|
char *Name;
|
||||||
|
char *PowerSourceState;
|
||||||
|
char *Type;
|
||||||
|
char *TransportType;
|
||||||
|
char *BatteryHealth;
|
||||||
|
char *HardwareSerialNumber;
|
||||||
|
|
||||||
|
int *PowerSourceID;
|
||||||
|
int *CurrentCapacity;
|
||||||
|
int *MaxCapacity;
|
||||||
|
int *DesignCapacity;
|
||||||
|
int *NominalCapacity;
|
||||||
|
|
||||||
|
int *TimeToEmpty;
|
||||||
|
int *TimeToFullCharge;
|
||||||
|
|
||||||
|
int *Voltage;
|
||||||
|
int *Current;
|
||||||
|
|
||||||
|
int *Temperature;
|
||||||
|
|
||||||
|
// boolean values
|
||||||
|
int *IsCharged;
|
||||||
|
int *IsCharging;
|
||||||
|
int *InternalFailure;
|
||||||
|
int *IsPresent;
|
||||||
|
};
|
||||||
|
|
||||||
|
int *CFDictionaryGetInt(CFDictionaryRef theDict, const void *key) {
|
||||||
|
CFNumberRef tmp;
|
||||||
|
int *value;
|
||||||
|
|
||||||
|
tmp = CFDictionaryGetValue(theDict, key);
|
||||||
|
|
||||||
|
if (tmp == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
value = (int*)malloc(sizeof(int));
|
||||||
|
if (CFNumberGetValue(tmp, kCFNumberIntType, value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *CFDictionaryGetBoolean(CFDictionaryRef theDict, const void *key) {
|
||||||
|
CFBooleanRef tmp;
|
||||||
|
int *value;
|
||||||
|
|
||||||
|
tmp = CFDictionaryGetValue(theDict, key);
|
||||||
|
|
||||||
|
if (tmp == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
value = (int*)malloc(sizeof(int));
|
||||||
|
if (CFBooleanGetValue(tmp)) {
|
||||||
|
*value = 1;
|
||||||
|
} else {
|
||||||
|
*value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *CFDictionaryGetSring(CFDictionaryRef theDict, const void *key) {
|
||||||
|
CFStringRef tmp;
|
||||||
|
CFIndex size;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
tmp = CFDictionaryGetValue(theDict, key);
|
||||||
|
|
||||||
|
if (tmp == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = CFStringGetLength(tmp) + 1;
|
||||||
|
value = (char*)malloc(size);
|
||||||
|
|
||||||
|
if(CFStringGetCString(tmp, value, size, kCFStringEncodingUTF8)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct macos_powersupply* getPowerSupplyInfo(CFDictionaryRef powerSourceInformation) {
|
||||||
|
struct macos_powersupply *ret;
|
||||||
|
|
||||||
|
if (powerSourceInformation == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret = (struct macos_powersupply*)malloc(sizeof(struct macos_powersupply));
|
||||||
|
|
||||||
|
ret->PowerSourceID = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSPowerSourceIDKey));
|
||||||
|
ret->CurrentCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSCurrentCapacityKey));
|
||||||
|
ret->MaxCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSMaxCapacityKey));
|
||||||
|
ret->DesignCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSDesignCapacityKey));
|
||||||
|
ret->NominalCapacity = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSNominalCapacityKey));
|
||||||
|
ret->TimeToEmpty = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSTimeToEmptyKey));
|
||||||
|
ret->TimeToFullCharge = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSTimeToFullChargeKey));
|
||||||
|
ret->Voltage = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSVoltageKey));
|
||||||
|
ret->Current = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSCurrentKey));
|
||||||
|
ret->Temperature = CFDictionaryGetInt(powerSourceInformation, CFSTR(kIOPSTemperatureKey));
|
||||||
|
|
||||||
|
ret->Name = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSNameKey));
|
||||||
|
ret->PowerSourceState = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSPowerSourceStateKey));
|
||||||
|
ret->Type = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSTypeKey));
|
||||||
|
ret->TransportType = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSTransportTypeKey));
|
||||||
|
ret->BatteryHealth = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSBatteryHealthKey));
|
||||||
|
ret->HardwareSerialNumber = CFDictionaryGetSring(powerSourceInformation, CFSTR(kIOPSHardwareSerialNumberKey));
|
||||||
|
|
||||||
|
ret->IsCharged = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSIsChargedKey));
|
||||||
|
ret->IsCharging = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSIsChargingKey));
|
||||||
|
ret->InternalFailure = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSInternalFailureKey));
|
||||||
|
ret->IsPresent = CFDictionaryGetBoolean(powerSourceInformation, CFSTR(kIOPSIsPresentKey));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void releasePowerSupply(struct macos_powersupply *ps) {
|
||||||
|
free(ps->Name);
|
||||||
|
free(ps->PowerSourceState);
|
||||||
|
free(ps->Type);
|
||||||
|
free(ps->TransportType);
|
||||||
|
free(ps->BatteryHealth);
|
||||||
|
free(ps->HardwareSerialNumber);
|
||||||
|
|
||||||
|
free(ps->PowerSourceID);
|
||||||
|
free(ps->CurrentCapacity);
|
||||||
|
free(ps->MaxCapacity);
|
||||||
|
free(ps->DesignCapacity);
|
||||||
|
free(ps->NominalCapacity);
|
||||||
|
free(ps->TimeToEmpty);
|
||||||
|
free(ps->TimeToFullCharge);
|
||||||
|
free(ps->Voltage);
|
||||||
|
free(ps->Current);
|
||||||
|
free(ps->Temperature);
|
||||||
|
|
||||||
|
free(ps->IsCharged);
|
||||||
|
free(ps->IsCharging);
|
||||||
|
free(ps->InternalFailure);
|
||||||
|
free(ps->IsPresent);
|
||||||
|
|
||||||
|
free(ps);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error {
|
||||||
|
psList, err := getPowerSourceList()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't get IOPPowerSourcesList: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range psList {
|
||||||
|
labels := getPowerSourceDescriptorLabels(info)
|
||||||
|
powerSupplyName := labels["power_supply"]
|
||||||
|
|
||||||
|
if c.ignoredPattern.MatchString(powerSupplyName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, value := range getPowerSourceDescriptorMap(info) {
|
||||||
|
if value == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(namespace, c.subsystem, name),
|
||||||
|
fmt.Sprintf("IOKit Power Source information field %s for <power_supply>.", name),
|
||||||
|
[]string{"power_supply"}, nil,
|
||||||
|
),
|
||||||
|
prometheus.GaugeValue, *value, powerSupplyName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pushEnumMetric(
|
||||||
|
ch,
|
||||||
|
getPowerSourceDescriptorState(info),
|
||||||
|
"power_source_state",
|
||||||
|
c.subsystem,
|
||||||
|
powerSupplyName,
|
||||||
|
)
|
||||||
|
|
||||||
|
pushEnumMetric(
|
||||||
|
ch,
|
||||||
|
getPowerSourceDescriptorBatteryHealth(info),
|
||||||
|
"battery_health",
|
||||||
|
c.subsystem,
|
||||||
|
powerSupplyName,
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
keys []string
|
||||||
|
values []string
|
||||||
|
)
|
||||||
|
for name, value := range labels {
|
||||||
|
if value != "" {
|
||||||
|
keys = append(keys, name)
|
||||||
|
values = append(values, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldDesc := prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(namespace, c.subsystem, "info"),
|
||||||
|
"IOKit Power Source information for <power_supply>.",
|
||||||
|
keys,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...)
|
||||||
|
|
||||||
|
C.releasePowerSupply(info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPowerSourceList fetches information from IOKit APIs
|
||||||
|
//
|
||||||
|
// Data is provided as opaque CoreFoundation references
|
||||||
|
// C.getPowerSupplyInfo will convert those objects in something
|
||||||
|
// easily manageable in Go.
|
||||||
|
// https://developer.apple.com/documentation/iokit/iopowersources_h
|
||||||
|
func getPowerSourceList() ([]*C.struct_macos_powersupply, error) {
|
||||||
|
infos, err := C.IOPSCopyPowerSourcesInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer C.CFRelease(infos)
|
||||||
|
|
||||||
|
psList, err := C.IOPSCopyPowerSourcesList(infos)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if psList == C.CFArrayRef(0) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
defer C.CFRelease(C.CFTypeRef(psList))
|
||||||
|
|
||||||
|
size, err := C.CFArrayGetCount(psList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make([]*C.struct_macos_powersupply, size)
|
||||||
|
for i := C.CFIndex(0); i < size; i++ {
|
||||||
|
ps, err := C.CFArrayGetValueAtIndex(psList, i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dict, err := C.IOPSGetPowerSourceDescription(infos, (C.CFTypeRef)(ps))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := C.getPowerSupplyInfo(dict)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret[int(i)] = info
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPowerSourceDescriptorMap(info *C.struct_macos_powersupply) map[string]*float64 {
|
||||||
|
return map[string]*float64{
|
||||||
|
"current_capacity": convertValue(info.CurrentCapacity),
|
||||||
|
"max_capacity": convertValue(info.MaxCapacity),
|
||||||
|
"design_capacity": convertValue(info.DesignCapacity),
|
||||||
|
"nominal_capacity": convertValue(info.NominalCapacity),
|
||||||
|
"time_to_empty_seconds": minutesToSeconds(info.TimeToEmpty),
|
||||||
|
"time_to_full_seconds": minutesToSeconds(info.TimeToFullCharge),
|
||||||
|
"voltage_volt": scaleValue(info.Voltage, 1e3),
|
||||||
|
"current_ampere": scaleValue(info.Current, 1e3),
|
||||||
|
"temp_celsius": convertValue(info.Temperature),
|
||||||
|
"present": convertValue(info.IsPresent),
|
||||||
|
"charging": convertValue(info.IsCharging),
|
||||||
|
"charged": convertValue(info.IsCharged),
|
||||||
|
"internal_failure": convertValue(info.InternalFailure),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPowerSourceDescriptorLabels(info *C.struct_macos_powersupply) map[string]string {
|
||||||
|
return map[string]string{
|
||||||
|
"id": strconv.FormatInt(int64(*info.PowerSourceID), 10),
|
||||||
|
"power_supply": C.GoString(info.Name),
|
||||||
|
"type": C.GoString(info.Type),
|
||||||
|
"transport_type": C.GoString(info.TransportType),
|
||||||
|
"serial_number": C.GoString(info.HardwareSerialNumber),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPowerSourceDescriptorState(info *C.struct_macos_powersupply) map[string]float64 {
|
||||||
|
stateMap := map[string]float64{
|
||||||
|
"Off Line": 0,
|
||||||
|
"AC Power": 0,
|
||||||
|
"Battery Power": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This field is always present
|
||||||
|
// https://developer.apple.com/documentation/iokit/kiopspowersourcestatekey
|
||||||
|
stateMap[C.GoString(info.PowerSourceState)] = 1
|
||||||
|
|
||||||
|
return stateMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPowerSourceDescriptorBatteryHealth(info *C.struct_macos_powersupply) map[string]float64 {
|
||||||
|
// This field is optional
|
||||||
|
// https://developer.apple.com/documentation/iokit/kiopsBatteryHealthkey
|
||||||
|
if info.BatteryHealth == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMap := map[string]float64{
|
||||||
|
"Good": 0,
|
||||||
|
"Fair": 0,
|
||||||
|
"Poor": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
stateMap[C.GoString(info.BatteryHealth)] = 1
|
||||||
|
|
||||||
|
return stateMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertValue(value *C.int) *float64 {
|
||||||
|
if value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := new(float64)
|
||||||
|
*ret = (float64)(*value)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func scaleValue(value *C.int, scale float64) *float64 {
|
||||||
|
ret := convertValue(value)
|
||||||
|
if ret == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret /= scale
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// minutesToSeconds converts *C.int minutes into *float64 seconds.
|
||||||
|
//
|
||||||
|
// Only positive values will be scaled to seconds, because negative ones
|
||||||
|
// have special meanings. I.e. -1 indicates "Still Calculating the Time"
|
||||||
|
func minutesToSeconds(minutes *C.int) *float64 {
|
||||||
|
ret := convertValue(minutes)
|
||||||
|
if ret == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if *ret > 0 {
|
||||||
|
*ret *= 60
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func pushEnumMetric(ch chan<- prometheus.Metric, values map[string]float64, name, subsystem, powerSupply string) {
|
||||||
|
for state, value := range values {
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(namespace, subsystem, name),
|
||||||
|
fmt.Sprintf("IOKit Power Source information field %s for <power_supply>.", name),
|
||||||
|
[]string{"power_supply", "state"}, nil,
|
||||||
|
),
|
||||||
|
prometheus.GaugeValue, value, powerSupply, state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
176
collector/powersupplyclass_linux.go
Normal file
176
collector/powersupplyclass_linux.go
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
// Copyright 2019 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 !nopowersupplyclass
|
||||||
|
// +build !nopowersupplyclass
|
||||||
|
|
||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/procfs/sysfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *powerSupplyClassCollector) Update(ch chan<- prometheus.Metric) error {
|
||||||
|
powerSupplyClass, err := getPowerSupplyClassInfo(c.ignoredPattern)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return ErrNoData
|
||||||
|
}
|
||||||
|
return fmt.Errorf("could not get power_supply class info: %w", err)
|
||||||
|
}
|
||||||
|
for _, powerSupply := range powerSupplyClass {
|
||||||
|
|
||||||
|
for name, value := range map[string]*int64{
|
||||||
|
"authentic": powerSupply.Authentic,
|
||||||
|
"calibrate": powerSupply.Calibrate,
|
||||||
|
"capacity": powerSupply.Capacity,
|
||||||
|
"capacity_alert_max": powerSupply.CapacityAlertMax,
|
||||||
|
"capacity_alert_min": powerSupply.CapacityAlertMin,
|
||||||
|
"cyclecount": powerSupply.CycleCount,
|
||||||
|
"online": powerSupply.Online,
|
||||||
|
"present": powerSupply.Present,
|
||||||
|
"time_to_empty_seconds": powerSupply.TimeToEmptyNow,
|
||||||
|
"time_to_full_seconds": powerSupply.TimeToFullNow,
|
||||||
|
} {
|
||||||
|
if value != nil {
|
||||||
|
pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value), powerSupply.Name, prometheus.GaugeValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, value := range map[string]*int64{
|
||||||
|
"current_boot": powerSupply.CurrentBoot,
|
||||||
|
"current_max": powerSupply.CurrentMax,
|
||||||
|
"current_ampere": powerSupply.CurrentNow,
|
||||||
|
"energy_empty": powerSupply.EnergyEmpty,
|
||||||
|
"energy_empty_design": powerSupply.EnergyEmptyDesign,
|
||||||
|
"energy_full": powerSupply.EnergyFull,
|
||||||
|
"energy_full_design": powerSupply.EnergyFullDesign,
|
||||||
|
"energy_watthour": powerSupply.EnergyNow,
|
||||||
|
"voltage_boot": powerSupply.VoltageBoot,
|
||||||
|
"voltage_max": powerSupply.VoltageMax,
|
||||||
|
"voltage_max_design": powerSupply.VoltageMaxDesign,
|
||||||
|
"voltage_min": powerSupply.VoltageMin,
|
||||||
|
"voltage_min_design": powerSupply.VoltageMinDesign,
|
||||||
|
"voltage_volt": powerSupply.VoltageNow,
|
||||||
|
"voltage_ocv": powerSupply.VoltageOCV,
|
||||||
|
"charge_control_limit": powerSupply.ChargeControlLimit,
|
||||||
|
"charge_control_limit_max": powerSupply.ChargeControlLimitMax,
|
||||||
|
"charge_counter": powerSupply.ChargeCounter,
|
||||||
|
"charge_empty": powerSupply.ChargeEmpty,
|
||||||
|
"charge_empty_design": powerSupply.ChargeEmptyDesign,
|
||||||
|
"charge_full": powerSupply.ChargeFull,
|
||||||
|
"charge_full_design": powerSupply.ChargeFullDesign,
|
||||||
|
"charge_ampere": powerSupply.ChargeNow,
|
||||||
|
"charge_term_current": powerSupply.ChargeTermCurrent,
|
||||||
|
"constant_charge_current": powerSupply.ConstantChargeCurrent,
|
||||||
|
"constant_charge_current_max": powerSupply.ConstantChargeCurrentMax,
|
||||||
|
"constant_charge_voltage": powerSupply.ConstantChargeVoltage,
|
||||||
|
"constant_charge_voltage_max": powerSupply.ConstantChargeVoltageMax,
|
||||||
|
"precharge_current": powerSupply.PrechargeCurrent,
|
||||||
|
"input_current_limit": powerSupply.InputCurrentLimit,
|
||||||
|
"power_watt": powerSupply.PowerNow,
|
||||||
|
} {
|
||||||
|
if value != nil {
|
||||||
|
pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/1e6, powerSupply.Name, prometheus.GaugeValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, value := range map[string]*int64{
|
||||||
|
"temp_celsius": powerSupply.Temp,
|
||||||
|
"temp_alert_max_celsius": powerSupply.TempAlertMax,
|
||||||
|
"temp_alert_min_celsius": powerSupply.TempAlertMin,
|
||||||
|
"temp_ambient_celsius": powerSupply.TempAmbient,
|
||||||
|
"temp_ambient_max_celsius": powerSupply.TempAmbientMax,
|
||||||
|
"temp_ambient_min_celsius": powerSupply.TempAmbientMin,
|
||||||
|
"temp_max_celsius": powerSupply.TempMax,
|
||||||
|
"temp_min_celsius": powerSupply.TempMin,
|
||||||
|
} {
|
||||||
|
if value != nil {
|
||||||
|
pushPowerSupplyMetric(ch, c.subsystem, name, float64(*value)/10.0, powerSupply.Name, prometheus.GaugeValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
keys []string
|
||||||
|
values []string
|
||||||
|
)
|
||||||
|
for name, value := range map[string]string{
|
||||||
|
"power_supply": powerSupply.Name,
|
||||||
|
"capacity_level": powerSupply.CapacityLevel,
|
||||||
|
"charge_type": powerSupply.ChargeType,
|
||||||
|
"health": powerSupply.Health,
|
||||||
|
"manufacturer": powerSupply.Manufacturer,
|
||||||
|
"model_name": powerSupply.ModelName,
|
||||||
|
"serial_number": powerSupply.SerialNumber,
|
||||||
|
"status": powerSupply.Status,
|
||||||
|
"technology": powerSupply.Technology,
|
||||||
|
"type": powerSupply.Type,
|
||||||
|
"usb_type": powerSupply.UsbType,
|
||||||
|
"scope": powerSupply.Scope,
|
||||||
|
} {
|
||||||
|
if value != "" {
|
||||||
|
keys = append(keys, name)
|
||||||
|
values = append(values, strings.ToValidUTF8(value, "<22>"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldDesc := prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(namespace, c.subsystem, "info"),
|
||||||
|
"info of /sys/class/power_supply/<power_supply>.",
|
||||||
|
keys,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(fieldDesc, prometheus.GaugeValue, 1.0, values...)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pushPowerSupplyMetric(ch chan<- prometheus.Metric, subsystem string, name string, value float64, powerSupplyName string, valueType prometheus.ValueType) {
|
||||||
|
fieldDesc := prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(namespace, subsystem, name),
|
||||||
|
fmt.Sprintf("%s value of /sys/class/power_supply/<power_supply>.", name),
|
||||||
|
[]string{"power_supply"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(fieldDesc, valueType, value, powerSupplyName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPowerSupplyClassInfo(ignore *regexp.Regexp) (sysfs.PowerSupplyClass, error) {
|
||||||
|
fs, err := sysfs.NewFS(*sysPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
powerSupplyClass, err := fs.PowerSupplyClass()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return powerSupplyClass, fmt.Errorf("error obtaining power_supply class info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for device := range powerSupplyClass {
|
||||||
|
if ignore.MatchString(device) {
|
||||||
|
delete(powerSupplyClass, device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return powerSupplyClass, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user