telegraf/plugins/inputs/intel_powerstat/intel_powerstat_test.go

495 lines
17 KiB
Go

// +build linux
package intel_powerstat
import (
"errors"
"strconv"
"sync"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/influxdata/telegraf/testutil"
)
func TestInitPlugin(t *testing.T) {
cores := []string{"cpu0", "cpu1", "cpu2", "cpu3"}
power, fsMock, _, _ := getPowerWithMockedServices()
fsMock.On("getCPUInfoStats", mock.Anything).
Return(nil, errors.New("error getting cpu stats")).Once()
require.Error(t, power.Init())
fsMock.On("getCPUInfoStats", mock.Anything).
Return(make(map[string]*cpuInfo), nil).Once()
require.Error(t, power.Init())
fsMock.On("getCPUInfoStats", mock.Anything).
Return(map[string]*cpuInfo{"0": {
vendorID: "GenuineIntel",
cpuFamily: "test",
}}, nil).Once()
require.Error(t, power.Init())
fsMock.On("getStringsMatchingPatternOnPath", mock.Anything).
Return(cores, nil).Once().
On("getCPUInfoStats", mock.Anything).
Return(map[string]*cpuInfo{"0": {
vendorID: "GenuineIntel",
cpuFamily: "6",
}}, nil)
// Verify MSR service initialization.
power.cpuFrequency = true
require.NoError(t, power.Init())
fsMock.AssertCalled(t, "getStringsMatchingPatternOnPath", mock.Anything)
require.Equal(t, len(cores), len(power.msr.getCPUCoresData()))
fsMock.On("getStringsMatchingPatternOnPath", mock.Anything).
Return(nil, errors.New("error during getStringsMatchingPatternOnPath")).Once()
// In case of an error when fetching cpu cores plugin should proceed with execution.
require.NoError(t, power.Init())
fsMock.AssertCalled(t, "getStringsMatchingPatternOnPath", mock.Anything)
require.Equal(t, 0, len(power.msr.getCPUCoresData()))
}
func TestParseCPUMetricsConfig(t *testing.T) {
power, _, _, _ := getPowerWithMockedServices()
disableCoreMetrics(power)
power.CPUMetrics = []string{
"cpu_frequency", "cpu_c1_state_residency", "cpu_c6_state_residency", "cpu_busy_cycles", "cpu_temperature",
"cpu_busy_frequency",
}
power.parseCPUMetricsConfig()
verifyCoreMetrics(t, power, true)
disableCoreMetrics(power)
verifyCoreMetrics(t, power, false)
power.CPUMetrics = []string{}
power.parseCPUMetricsConfig()
power.CPUMetrics = []string{"cpu_c6_state_residency", "#@$sdkjdfsdf3@", "1pu_c1_state_residency"}
power.parseCPUMetricsConfig()
require.Equal(t, false, power.cpuC1StateResidency)
require.Equal(t, true, power.cpuC6StateResidency)
disableCoreMetrics(power)
verifyCoreMetrics(t, power, false)
power.CPUMetrics = []string{"#@$sdkjdfsdf3@", "1pu_c1_state_residency", "123"}
power.parseCPUMetricsConfig()
verifyCoreMetrics(t, power, false)
}
func verifyCoreMetrics(t *testing.T, power *PowerStat, enabled bool) {
require.Equal(t, enabled, power.cpuFrequency)
require.Equal(t, enabled, power.cpuC1StateResidency)
require.Equal(t, enabled, power.cpuC6StateResidency)
require.Equal(t, enabled, power.cpuBusyCycles)
require.Equal(t, enabled, power.cpuBusyFrequency)
require.Equal(t, enabled, power.cpuTemperature)
}
func TestGather(t *testing.T) {
var acc testutil.Accumulator
packageIDs := []string{"0", "1"}
coreIDs := []string{"0", "1", "2", "3"}
socketCurrentEnergy := 13213852.2
dramCurrentEnergy := 784552.0
preparedCPUData := getPreparedCPUData(coreIDs)
raplDataMap := prepareRaplDataMap(packageIDs, socketCurrentEnergy, dramCurrentEnergy)
power, _, raplMock, msrMock := getPowerWithMockedServices()
prepareCPUInfo(power, coreIDs, packageIDs)
enableCoreMetrics(power)
power.skipFirstIteration = false
raplMock.On("initializeRaplData", mock.Anything).
On("getRaplData").Return(raplDataMap).
On("retrieveAndCalculateData", mock.Anything).Return(nil).Times(len(raplDataMap)).
On("getConstraintMaxPowerWatts", mock.Anything).Return(546783852.3, nil)
msrMock.On("getCPUCoresData").Return(preparedCPUData).
On("openAndReadMsr", mock.Anything).Return(nil).
On("retrieveCPUFrequencyForCore", mock.Anything).Return(1200000.2, nil)
require.NoError(t, power.Gather(&acc))
// Number of global metrics : 3
// Number of per core metrics : 6
require.Equal(t, 3*len(packageIDs)+6*len(coreIDs), len(acc.GetTelegrafMetrics()))
}
func TestAddGlobalMetricsNegative(t *testing.T) {
var acc testutil.Accumulator
socketCurrentEnergy := 13213852.2
dramCurrentEnergy := 784552.0
raplDataMap := prepareRaplDataMap([]string{"0", "1"}, socketCurrentEnergy, dramCurrentEnergy)
power, _, raplMock, _ := getPowerWithMockedServices()
power.skipFirstIteration = false
raplMock.On("initializeRaplData", mock.Anything).Once().
On("getRaplData").Return(raplDataMap).Once().
On("retrieveAndCalculateData", mock.Anything).Return(errors.New("error while calculating data")).Times(len(raplDataMap))
power.addGlobalMetrics(&acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
raplMock.AssertNumberOfCalls(t, "retrieveAndCalculateData", len(raplDataMap))
raplMock.On("initializeRaplData", mock.Anything).Once().
On("getRaplData").Return(make(map[string]*raplData)).Once()
power.addGlobalMetrics(&acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
raplMock.AssertNotCalled(t, "retrieveAndCalculateData")
raplMock.On("initializeRaplData", mock.Anything).Once().
On("getRaplData").Return(raplDataMap).
On("retrieveAndCalculateData", mock.Anything).Return(nil).Once().
On("retrieveAndCalculateData", mock.Anything).Return(errors.New("error while calculating data")).Once().
On("getConstraintMaxPowerWatts", mock.Anything).Return(12313851.5, nil).Twice()
power.addGlobalMetrics(&acc)
require.Equal(t, 3, len(acc.GetTelegrafMetrics()))
}
func TestAddGlobalMetricsPositive(t *testing.T) {
var acc testutil.Accumulator
socketCurrentEnergy := 3644574.4
dramCurrentEnergy := 124234872.5
raplDataMap := prepareRaplDataMap([]string{"0", "1"}, socketCurrentEnergy, dramCurrentEnergy)
maxPower := 546783852.9
power, _, raplMock, _ := getPowerWithMockedServices()
power.skipFirstIteration = false
raplMock.On("initializeRaplData", mock.Anything).
On("getRaplData").Return(raplDataMap).
On("retrieveAndCalculateData", mock.Anything).Return(nil).Times(len(raplDataMap)).
On("getConstraintMaxPowerWatts", mock.Anything).Return(maxPower, nil).Twice().
On("getCurrentDramPowerConsumption", mock.Anything).Return(dramCurrentEnergy)
power.addGlobalMetrics(&acc)
require.Equal(t, 6, len(acc.GetTelegrafMetrics()))
expectedResults := getGlobalMetrics(maxPower, socketCurrentEnergy, dramCurrentEnergy)
for _, test := range expectedResults {
acc.AssertContainsTaggedFields(t, "powerstat_package", test.fields, test.tags)
}
}
func TestAddMetricsForSingleCoreNegative(t *testing.T) {
var wg sync.WaitGroup
var acc testutil.Accumulator
core := "0"
power, _, _, msrMock := getPowerWithMockedServices()
msrMock.On("openAndReadMsr", core).Return(errors.New("error reading MSR file")).Once()
// Skip generating metric for CPU frequency.
power.cpuFrequency = false
wg.Add(1)
power.addMetricsForSingleCore(core, &acc, &wg)
wg.Wait()
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
}
func TestAddCPUFrequencyMetric(t *testing.T) {
var acc testutil.Accumulator
cpuID := "1"
coreID := "3"
packageID := "0"
frequency := 1200000.2
power, _, _, msrMock := getPowerWithMockedServices()
prepareCPUInfoForSingleCPU(power, cpuID, coreID, packageID)
msrMock.On("retrieveCPUFrequencyForCore", mock.Anything).
Return(float64(0), errors.New("error on reading file")).Once()
power.addCPUFrequencyMetric(cpuID, &acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
msrMock.On("retrieveCPUFrequencyForCore", mock.Anything).Return(frequency, nil).Once()
power.addCPUFrequencyMetric(cpuID, &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
expectedFrequency := roundFloatToNearestTwoDecimalPlaces(frequency)
expectedMetric := getPowerCoreMetric("cpu_frequency_mhz", expectedFrequency, coreID, packageID, cpuID)
acc.AssertContainsTaggedFields(t, "powerstat_core", expectedMetric.fields, expectedMetric.tags)
}
func TestAddCoreCPUTemperatureMetric(t *testing.T) {
var acc testutil.Accumulator
cpuID := "0"
coreID := "2"
packageID := "1"
power, _, _, msrMock := getPowerWithMockedServices()
preparedData := getPreparedCPUData([]string{cpuID})
expectedTemp := preparedData[cpuID].throttleTemp - preparedData[cpuID].temp
prepareCPUInfoForSingleCPU(power, cpuID, coreID, packageID)
msrMock.On("getCPUCoresData").Return(preparedData).Once()
power.addCPUTemperatureMetric(cpuID, &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
expectedMetric := getPowerCoreMetric("cpu_temperature_celsius", expectedTemp, coreID, packageID, cpuID)
acc.AssertContainsTaggedFields(t, "powerstat_core", expectedMetric.fields, expectedMetric.tags)
}
func TestAddC6StateResidencyMetric(t *testing.T) {
var acc testutil.Accumulator
cpuID := "0"
coreID := "2"
packageID := "1"
power, _, _, msrMock := getPowerWithMockedServices()
prepareCPUInfoForSingleCPU(power, cpuID, coreID, packageID)
preparedData := getPreparedCPUData([]string{cpuID})
expectedC6 := roundFloatToNearestTwoDecimalPlaces(percentageMultiplier *
float64(preparedData[cpuID].c6Delta) / float64(preparedData[cpuID].timeStampCounterDelta))
msrMock.On("getCPUCoresData").Return(preparedData).Twice()
power.addCPUC6StateResidencyMetric(cpuID, &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
expectedMetric := getPowerCoreMetric("cpu_c6_state_residency_percent", expectedC6, coreID, packageID, cpuID)
acc.AssertContainsTaggedFields(t, "powerstat_core", expectedMetric.fields, expectedMetric.tags)
acc.ClearMetrics()
preparedData[cpuID].timeStampCounterDelta = 0
power.addCPUC6StateResidencyMetric(cpuID, &acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
}
func TestAddProcessorBusyCyclesMetric(t *testing.T) {
var acc testutil.Accumulator
cpuID := "0"
coreID := "2"
packageID := "1"
power, _, _, msrMock := getPowerWithMockedServices()
prepareCPUInfoForSingleCPU(power, cpuID, coreID, packageID)
preparedData := getPreparedCPUData([]string{cpuID})
expectedBusyCycles := roundFloatToNearestTwoDecimalPlaces(percentageMultiplier * float64(preparedData[cpuID].mperfDelta) /
float64(preparedData[cpuID].timeStampCounterDelta))
msrMock.On("getCPUCoresData").Return(preparedData).Twice()
power.addCPUBusyCyclesMetric(cpuID, &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
expectedMetric := getPowerCoreMetric("cpu_busy_cycles_percent", expectedBusyCycles, coreID, packageID, cpuID)
acc.AssertContainsTaggedFields(t, "powerstat_core", expectedMetric.fields, expectedMetric.tags)
acc.ClearMetrics()
preparedData[cpuID].timeStampCounterDelta = 0
power.addCPUBusyCyclesMetric(cpuID, &acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
}
func TestAddProcessorBusyFrequencyMetric(t *testing.T) {
var acc testutil.Accumulator
cpuID := "0"
coreID := "2"
packageID := "1"
power, _, _, msrMock := getPowerWithMockedServices()
prepareCPUInfoForSingleCPU(power, cpuID, coreID, packageID)
preparedData := getPreparedCPUData([]string{cpuID})
power.skipFirstIteration = false
msrMock.On("getCPUCoresData").Return(preparedData).Twice()
power.addCPUBusyFrequencyMetric(cpuID, &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
acc.ClearMetrics()
preparedData[cpuID].mperfDelta = 0
power.addCPUBusyFrequencyMetric(cpuID, &acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
}
func TestAddC1StateResidencyMetric(t *testing.T) {
var acc testutil.Accumulator
cpuID := "0"
coreID := "2"
packageID := "1"
power, _, _, msrMock := getPowerWithMockedServices()
prepareCPUInfoForSingleCPU(power, cpuID, coreID, packageID)
preparedData := getPreparedCPUData([]string{cpuID})
c1 := preparedData[cpuID].timeStampCounterDelta - preparedData[cpuID].mperfDelta - preparedData[cpuID].c3Delta -
preparedData[cpuID].c6Delta - preparedData[cpuID].c7Delta
expectedC1 := roundFloatToNearestTwoDecimalPlaces(percentageMultiplier * float64(c1) / float64(preparedData[cpuID].timeStampCounterDelta))
msrMock.On("getCPUCoresData").Return(preparedData).Twice()
power.addCPUC1StateResidencyMetric(cpuID, &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
expectedMetric := getPowerCoreMetric("cpu_c1_state_residency_percent", expectedC1, coreID, packageID, cpuID)
acc.AssertContainsTaggedFields(t, "powerstat_core", expectedMetric.fields, expectedMetric.tags)
acc.ClearMetrics()
preparedData[cpuID].timeStampCounterDelta = 0
power.addCPUC1StateResidencyMetric(cpuID, &acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
}
func TestAddThermalDesignPowerMetric(t *testing.T) {
var acc testutil.Accumulator
sockets := []string{"0"}
maxPower := 195720672.1
power, _, raplMock, _ := getPowerWithMockedServices()
raplMock.On("getConstraintMaxPowerWatts", mock.Anything).
Return(float64(0), errors.New("getConstraintMaxPowerWatts error")).Once().
On("getConstraintMaxPowerWatts", mock.Anything).Return(maxPower, nil).Once()
power.addThermalDesignPowerMetric(sockets[0], &acc)
require.Equal(t, 0, len(acc.GetTelegrafMetrics()))
power.addThermalDesignPowerMetric(sockets[0], &acc)
require.Equal(t, 1, len(acc.GetTelegrafMetrics()))
expectedTDP := roundFloatToNearestTwoDecimalPlaces(maxPower)
expectedMetric := getPowerGlobalMetric("thermal_design_power_watts", expectedTDP, sockets[0])
acc.AssertContainsTaggedFields(t, "powerstat_package", expectedMetric.fields, expectedMetric.tags)
}
func getPreparedCPUData(cores []string) map[string]*msrData {
msrDataMap := make(map[string]*msrData)
for _, core := range cores {
msrDataMap[core] = &msrData{
mperf: 43079,
aperf: 82001,
timeStampCounter: 15514,
c3: 52829,
c6: 86930,
c7: 25340,
throttleTemp: 88150,
temp: 40827,
mperfDelta: 23515,
aperfDelta: 33866,
timeStampCounterDelta: 13686000,
c3Delta: 20003,
c6Delta: 44518,
c7Delta: 20979,
}
}
return msrDataMap
}
func getGlobalMetrics(maxPower float64, socketCurrentEnergy float64, dramCurrentEnergy float64) []struct {
fields map[string]interface{}
tags map[string]string
} {
return []struct {
fields map[string]interface{}
tags map[string]string
}{
getPowerGlobalMetric("thermal_design_power_watts", roundFloatToNearestTwoDecimalPlaces(maxPower), "0"),
getPowerGlobalMetric("thermal_design_power_watts", roundFloatToNearestTwoDecimalPlaces(maxPower), "1"),
getPowerGlobalMetric("current_power_consumption_watts", roundFloatToNearestTwoDecimalPlaces(socketCurrentEnergy), "0"),
getPowerGlobalMetric("current_power_consumption_watts", roundFloatToNearestTwoDecimalPlaces(socketCurrentEnergy), "1"),
getPowerGlobalMetric("current_dram_power_consumption_watts", roundFloatToNearestTwoDecimalPlaces(dramCurrentEnergy), "0"),
getPowerGlobalMetric("current_dram_power_consumption_watts", roundFloatToNearestTwoDecimalPlaces(dramCurrentEnergy), "1"),
}
}
func getPowerCoreMetric(name string, value interface{}, coreID string, packageID string, cpuID string) struct {
fields map[string]interface{}
tags map[string]string
} {
return getPowerMetric(name, value, map[string]string{"package_id": packageID, "core_id": coreID, "cpu_id": cpuID})
}
func getPowerGlobalMetric(name string, value interface{}, socketID string) struct {
fields map[string]interface{}
tags map[string]string
} {
return getPowerMetric(name, value, map[string]string{"package_id": socketID})
}
func getPowerMetric(name string, value interface{}, tags map[string]string) struct {
fields map[string]interface{}
tags map[string]string
} {
return struct {
fields map[string]interface{}
tags map[string]string
}{
map[string]interface{}{
name: value,
},
tags,
}
}
func prepareCPUInfoForSingleCPU(power *PowerStat, cpuID string, coreID string, packageID string) {
power.cpuInfo = make(map[string]*cpuInfo)
power.cpuInfo[cpuID] = &cpuInfo{
physicalID: packageID,
coreID: coreID,
cpuID: cpuID,
}
}
func prepareCPUInfo(power *PowerStat, coreIDs []string, packageIDs []string) {
power.cpuInfo = make(map[string]*cpuInfo)
currentCPU := 0
for _, packageID := range packageIDs {
for _, coreID := range coreIDs {
cpuID := strconv.Itoa(currentCPU)
power.cpuInfo[cpuID] = &cpuInfo{
physicalID: packageID,
cpuID: cpuID,
coreID: coreID,
}
currentCPU++
}
}
}
func enableCoreMetrics(power *PowerStat) {
power.cpuC1StateResidency = true
power.cpuC6StateResidency = true
power.cpuTemperature = true
power.cpuBusyFrequency = true
power.cpuFrequency = true
power.cpuBusyCycles = true
}
func disableCoreMetrics(power *PowerStat) {
power.cpuC1StateResidency = false
power.cpuC6StateResidency = false
power.cpuTemperature = false
power.cpuBusyFrequency = false
power.cpuFrequency = false
power.cpuBusyCycles = false
}
func prepareRaplDataMap(socketIDs []string, socketCurrentEnergy float64, dramCurrentEnergy float64) map[string]*raplData {
raplDataMap := make(map[string]*raplData, len(socketIDs))
for _, socketID := range socketIDs {
raplDataMap[socketID] = &raplData{
socketCurrentEnergy: socketCurrentEnergy,
dramCurrentEnergy: dramCurrentEnergy,
}
}
return raplDataMap
}
func getPowerWithMockedServices() (*PowerStat, *mockFileService, *mockRaplService, *mockMsrService) {
fsMock := &mockFileService{}
msrMock := &mockMsrService{}
raplMock := &mockRaplService{}
logger := testutil.Logger{Name: "PowerPluginTest"}
p := newPowerStat(fsMock)
p.Log = logger
p.fs = fsMock
p.rapl = raplMock
p.msr = msrMock
return p, fsMock, raplMock, msrMock
}