diff --git a/plugins/inputs/temp/temp_linux.go b/plugins/inputs/temp/temp_linux.go index 0bfe6b317..a7ef5fe15 100644 --- a/plugins/inputs/temp/temp_linux.go +++ b/plugins/inputs/temp/temp_linux.go @@ -56,26 +56,55 @@ func (t *Temperature) Gather(acc telegraf.Accumulator) error { } } - for _, temp := range temperatures { - acc.AddFields( - "temp", - map[string]interface{}{"temp": temp.Temperature}, - t.getTagsForTemperature(temp, "_input"), - ) + switch t.MetricFormat { + case "v1": + t.createMetricsV1(acc, temperatures) + case "v2": + t.createMetricsV2(acc, temperatures) + } + return nil +} + +func (t *Temperature) createMetricsV1(acc telegraf.Accumulator, temperatures []TemperatureStat) { + for _, temp := range temperatures { + sensor := temp.Name + if temp.Label != "" { + sensor += "_" + strings.ReplaceAll(temp.Label, " ", "") + } + + // Mandatory measurement value + tags := map[string]string{"sensor": sensor + "_input"} + if t.DeviceTag { + tags["device"] = temp.Device + } + acc.AddFields("temp", map[string]interface{}{"temp": temp.Temperature}, tags) + + // Optional values values for measurement, value := range temp.Additional { - fieldname := "temp" - if measurement == "alarm" { - fieldname = "active" + tags := map[string]string{"sensor": sensor + "_" + measurement} + if t.DeviceTag { + tags["device"] = temp.Device } - acc.AddFields( - "temp", - map[string]interface{}{fieldname: value}, - t.getTagsForTemperature(temp, "_"+measurement), - ) + acc.AddFields("temp", map[string]interface{}{"temp": value}, tags) } } - return nil +} + +func (t *Temperature) createMetricsV2(acc telegraf.Accumulator, temperatures []TemperatureStat) { + for _, temp := range temperatures { + sensor := temp.Name + if temp.Label != "" { + sensor += "_" + strings.ReplaceAll(temp.Label, " ", "_") + } + + // Mandatory measurement value + tags := map[string]string{"sensor": sensor} + if t.DeviceTag { + tags["device"] = temp.Device + } + acc.AddFields("temp", map[string]interface{}{"temp": temp.Temperature}, tags) + } } func (t *Temperature) gatherHwmon(syspath string) ([]TemperatureStat, error) { @@ -144,15 +173,6 @@ func (t *Temperature) gatherHwmon(syspath string) ([]TemperatureStat, error) { temp.Temperature = v / scalingFactor } - // Alarm (optional) - fn = filepath.Join(path, prefix+"_alarm") - buf, err = os.ReadFile(fn) - if err == nil { - if a, err := strconv.ParseBool(strings.TrimSpace(string(buf))); err == nil { - temp.Additional["alarm"] = a - } - } - // Read all possible values of the sensor matches, err := filepath.Glob(filepath.Join(path, prefix+"_*")) if err != nil { @@ -172,7 +192,7 @@ func (t *Temperature) gatherHwmon(syspath string) ([]TemperatureStat, error) { // Skip already added values switch measurement { - case "label", "input", "alarm": + case "label", "input": continue } @@ -229,21 +249,3 @@ func (t *Temperature) gatherThermalZone(syspath string) ([]TemperatureStat, erro return stats, nil } - -func (t *Temperature) getTagsForTemperature(temp TemperatureStat, suffix string) map[string]string { - sensor := temp.Name - if temp.Label != "" && suffix != "" { - switch t.MetricFormat { - case "v1": - sensor += "_" + strings.ReplaceAll(temp.Label, " ", "") + suffix - case "v2": - sensor += "_" + strings.ReplaceAll(temp.Label, " ", "_") + suffix - } - } - - tags := map[string]string{"sensor": sensor} - if t.DeviceTag { - tags["device"] = temp.Device - } - return tags -} diff --git a/plugins/inputs/temp/temp_test.go b/plugins/inputs/temp/temp_test.go index a53525ff4..729c3e733 100644 --- a/plugins/inputs/temp/temp_test.go +++ b/plugins/inputs/temp/temp_test.go @@ -3,19 +3,33 @@ package temp import ( + "fmt" "os" "path/filepath" + "strconv" + "strings" "testing" - "time" + "github.com/google/go-cmp/cmp" + "github.com/shirou/gopsutil/v3/host" "github.com/stretchr/testify/require" "github.com/influxdata/telegraf" - "github.com/influxdata/telegraf/metric" + "github.com/influxdata/telegraf/config" + "github.com/influxdata/telegraf/plugins/inputs" + "github.com/influxdata/telegraf/plugins/parsers/influx" "github.com/influxdata/telegraf/testutil" ) -func TestTemperatureInvaldiMetricFormat(t *testing.T) { +func TestDefaultMetricFormat(t *testing.T) { + plugin := &Temperature{ + Log: &testutil.Logger{}, + } + require.NoError(t, plugin.Init()) + require.Equal(t, "v2", plugin.MetricFormat) +} + +func TestInvalidMetricFormat(t *testing.T) { plugin := &Temperature{ MetricFormat: "foo", Log: &testutil.Logger{}, @@ -23,451 +37,306 @@ func TestTemperatureInvaldiMetricFormat(t *testing.T) { require.ErrorContains(t, plugin.Init(), "invalid 'metric_format'") } -func TestTemperatureMetricV1(t *testing.T) { - expected := []telegraf.Metric{ - // hwmon0 / temp1 - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_alarm"}, - map[string]interface{}{"active": false}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_crit"}, - map[string]interface{}{"temp": 84.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_input"}, - map[string]interface{}{"temp": 35.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_max"}, - map[string]interface{}{"temp": 81.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_min"}, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - // hwmon0 / temp2 - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor1_input"}, - map[string]interface{}{"temp": 35.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor1_max"}, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor1_min"}, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - // hwmon0 / temp3 - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor2_input"}, - map[string]interface{}{"temp": 38.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor2_max"}, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor2_min"}, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - // hwmon1 / temp1 - metric.New( - "temp", - map[string]string{"sensor": "k10temp_tctl_input"}, - map[string]interface{}{"temp": 33.25}, - time.Unix(0, 0), - ), - // hwmon1 / temp3 - metric.New( - "temp", - map[string]string{"sensor": "k10temp_tccd1_input"}, - map[string]interface{}{"temp": 33.25}, - time.Unix(0, 0), - ), - } - - require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testdata", "general", "sys"))) - - plugin := &Temperature{ - MetricFormat: "v1", - Log: &testutil.Logger{}, - } - require.NoError(t, plugin.Init()) - - var acc testutil.Accumulator - require.NoError(t, plugin.Gather(&acc)) - actual := acc.GetTelegrafMetrics() - testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime(), testutil.SortMetrics()) -} - -func TestTemperature(t *testing.T) { - expected := []telegraf.Metric{ - // hwmon0 / temp1 - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_alarm"}, - map[string]interface{}{"active": false}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_crit"}, - map[string]interface{}{"temp": 84.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_input"}, - map[string]interface{}{"temp": 35.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_max"}, - map[string]interface{}{"temp": 81.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_composite_min"}, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - // hwmon0 / temp2 - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor_1_input"}, - map[string]interface{}{"temp": 35.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor_1_max"}, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor_1_min"}, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - // hwmon0 / temp3 - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor_2_input"}, - map[string]interface{}{"temp": 38.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor_2_max"}, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{"sensor": "nvme_sensor_2_min"}, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - // hwmon1 / temp1 - metric.New( - "temp", - map[string]string{"sensor": "k10temp_tctl_input"}, - map[string]interface{}{"temp": 33.25}, - time.Unix(0, 0), - ), - // hwmon1 / temp3 - metric.New( - "temp", - map[string]string{"sensor": "k10temp_tccd1_input"}, - map[string]interface{}{"temp": 33.25}, - time.Unix(0, 0), - ), - } - - require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testdata", "general", "sys"))) +func TestNameCollisions(t *testing.T) { + require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testcases", "with_name", "sys"))) plugin := &Temperature{Log: &testutil.Logger{}} require.NoError(t, plugin.Init()) var acc testutil.Accumulator require.NoError(t, plugin.Gather(&acc)) - actual := acc.GetTelegrafMetrics() - testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime(), testutil.SortMetrics()) + require.Len(t, acc.GetTelegrafMetrics(), 8) } -func TestTemperatureNameCollisions(t *testing.T) { - require.NoError(t, os.Setenv("HOST_SYS", filepath.Join("testdata", "with_name", "sys"))) - plugin := &Temperature{Log: &testutil.Logger{}} - require.NoError(t, plugin.Init()) +func TestCases(t *testing.T) { + // Get all directories in testdata + folders, err := os.ReadDir("testcases") + require.NoError(t, err) - var acc testutil.Accumulator - require.NoError(t, plugin.Gather(&acc)) - require.Len(t, acc.GetTelegrafMetrics(), 24) + // Register the plugin + inputs.Add("temp", func() telegraf.Input { + return &Temperature{} + }) + + // Prepare the influx parser for expectations + parser := &influx.Parser{} + require.NoError(t, parser.Init()) + + for _, f := range folders { + // Only handle folders + if !f.IsDir() { + continue + } + // Compare options + options := []cmp.Option{ + testutil.IgnoreTime(), + testutil.SortMetrics(), + } + + // Test v1 + t.Run(f.Name()+"_v1", func(t *testing.T) { + testcasePath := filepath.Join("testcases", f.Name()) + configFilename := filepath.Join(testcasePath, "telegraf.conf") + expectedFilename := filepath.Join(testcasePath, "expected_v1.out") + + // Read the expected output if any + var expected []telegraf.Metric + if _, err := os.Stat(expectedFilename); err == nil { + var err error + expected, err = testutil.ParseMetricsFromFile(expectedFilename, parser) + require.NoError(t, err) + } + + // Prepare the environment + require.NoError(t, os.Setenv("HOST_SYS", filepath.Join(testcasePath, "sys"))) + + // Configure the plugin + cfg := config.NewConfig() + require.NoError(t, cfg.LoadConfig(configFilename)) + require.Len(t, cfg.Inputs, 1) + plugin := cfg.Inputs[0].Input.(*Temperature) + plugin.MetricFormat = "v1" + require.NoError(t, plugin.Init()) + + var acc testutil.Accumulator + require.NoError(t, plugin.Gather(&acc)) + + // Check the metric nevertheless as we might get some metrics despite errors. + actual := acc.GetTelegrafMetrics() + testutil.RequireMetricsEqual(t, expected, actual, options...) + }) + + // Test v2 + t.Run(f.Name()+"_v2", func(t *testing.T) { + testcasePath := filepath.Join("testcases", f.Name()) + configFilename := filepath.Join(testcasePath, "telegraf.conf") + expectedFilename := filepath.Join(testcasePath, "expected_v2.out") + + // Read the expected output if any + var expected []telegraf.Metric + if _, err := os.Stat(expectedFilename); err == nil { + var err error + expected, err = testutil.ParseMetricsFromFile(expectedFilename, parser) + require.NoError(t, err) + } + + // Prepare the environment + require.NoError(t, os.Setenv("HOST_SYS", filepath.Join(testcasePath, "sys"))) + + // Configure the plugin + cfg := config.NewConfig() + require.NoError(t, cfg.LoadConfig(configFilename)) + require.Len(t, cfg.Inputs, 1) + plugin := cfg.Inputs[0].Input.(*Temperature) + plugin.MetricFormat = "v2" + require.NoError(t, plugin.Init()) + + var acc testutil.Accumulator + require.NoError(t, plugin.Gather(&acc)) + + // Check the metric nevertheless as we might get some metrics despite errors. + actual := acc.GetTelegrafMetrics() + testutil.RequireMetricsEqual(t, expected, actual, options...) + }) + } } -func TestTemperatureWithDeviceTag(t *testing.T) { - expected := []telegraf.Metric{ - // hwmon0 / temp1 - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_input", - "device": "nvme0", - }, - map[string]interface{}{"temp": 32.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_alarm", - "device": "nvme0", - }, - map[string]interface{}{"active": false}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_crit", - "device": "nvme0", - }, - map[string]interface{}{"temp": 84.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_min", - "device": "nvme0", - }, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_max", - "device": "nvme0", - }, - map[string]interface{}{"temp": 81.85}, - time.Unix(0, 0), - ), - // hwmon0 / temp2 - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_1_input", - "device": "nvme0", - }, - map[string]interface{}{"temp": 32.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_1_min", - "device": "nvme0", - }, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_1_max", - "device": "nvme0", - }, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - // hwmon0 / temp3 - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_2_input", - "device": "nvme0", - }, - map[string]interface{}{"temp": 36.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_2_min", - "device": "nvme0", - }, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_2_max", - "device": "nvme0", - }, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - // hwmon1 / temp1 - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_input", - "device": "nvme1", - }, - map[string]interface{}{"temp": 35.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_alarm", - "device": "nvme1", - }, - map[string]interface{}{"active": false}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_crit", - "device": "nvme1", - }, - map[string]interface{}{"temp": 84.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_min", - "device": "nvme1", - }, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_composite_max", - "device": "nvme1", - }, - map[string]interface{}{"temp": 81.85}, - time.Unix(0, 0), - ), - // hwmon1 / temp2 - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_1_input", - "device": "nvme1", - }, - map[string]interface{}{"temp": 35.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_1_min", - "device": "nvme1", - }, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_1_max", - "device": "nvme1", - }, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - // hwmon1 / temp3 - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_2_input", - "device": "nvme1", - }, - map[string]interface{}{"temp": 37.85}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_2_min", - "device": "nvme1", - }, - map[string]interface{}{"temp": -273.15}, - time.Unix(0, 0), - ), - metric.New( - "temp", - map[string]string{ - "sensor": "nvme_sensor_2_max", - "device": "nvme1", - }, - map[string]interface{}{"temp": 65261.85}, - time.Unix(0, 0), - ), - // hwmon2 / temp1 - metric.New( - "temp", - map[string]string{ - "sensor": "k10temp_tctl_input", - "device": "0000:00:18.3", - }, - map[string]interface{}{"temp": 31.875}, - time.Unix(0, 0), - ), - // hwmon2 / temp3 - metric.New( - "temp", - map[string]string{ - "sensor": "k10temp_tccd1_input", - "device": "0000:00:18.3", - }, - map[string]interface{}{"temp": 30.75}, - time.Unix(0, 0), - ), +func TestRegression(t *testing.T) { + // Get all directories in testdata + folders, err := os.ReadDir("testcases") + require.NoError(t, err) + + // Register the plugin + inputs.Add("temp", func() telegraf.Input { + return &Temperature{} + }) + + // Prepare the influx parser for expectations + parser := &influx.Parser{} + require.NoError(t, parser.Init()) + + for _, f := range folders { + // Only handle folders + if !f.IsDir() { + continue + } + // Compare options + options := []cmp.Option{ + testutil.IgnoreTime(), + testutil.SortMetrics(), + } + + // Test v1 metrics + t.Run(f.Name()+"_v1", func(t *testing.T) { + testcasePath := filepath.Join("testcases", f.Name()) + actualFilename := filepath.Join(testcasePath, "expected_v1.out") + + // Read the expected output if any + var actual []telegraf.Metric + if _, err := os.Stat(actualFilename); err == nil { + var err error + actual, err = testutil.ParseMetricsFromFile(actualFilename, parser) + require.NoError(t, err) + } + + // Remove potential device-tags + for i := range actual { + actual[i].RemoveTag("device") + } + + // Use the