feat: add mock input plugin (#9782)
This commit is contained in:
parent
f38290f454
commit
2220fa2c2d
|
|
@ -111,6 +111,7 @@ import (
|
|||
_ "github.com/influxdata/telegraf/plugins/inputs/memcached"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/mesos"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/minecraft"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/mock"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/modbus"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/mongodb"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/monit"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
# Mock Data
|
||||
|
||||
The mock input plugin generates random data based on a selection of different
|
||||
algorithms. For example, it can produce random data between a set of values,
|
||||
fake stock data, sine waves, and step-wise values.
|
||||
|
||||
Additionally, users can set the measurement name and tags used to whatever is
|
||||
required to mock their situation.
|
||||
|
||||
## Configuration
|
||||
|
||||
The mock plugin only requires that:
|
||||
|
||||
1) Metric name is set
|
||||
2) One of the below data field algorithms is defined
|
||||
|
||||
Below is a sample config to generate one of each of the four types:
|
||||
|
||||
```toml
|
||||
[[inputs.mock]]
|
||||
## Set the metric name to use for reporting
|
||||
metric_name = "mock"
|
||||
|
||||
## Optional string key-value pairs of tags to add to all metrics
|
||||
# [inputs.mock.tags]
|
||||
# "key" = "value"
|
||||
|
||||
## One or more mock data fields *must* be defined.
|
||||
##
|
||||
## [[inputs.mock.random]]
|
||||
## name = "rand"
|
||||
## min = 1.0
|
||||
## max = 6.0
|
||||
## [[inputs.mock.sine_wave]]
|
||||
## name = "wave"
|
||||
## amplitude = 1.0
|
||||
## period = 0.5
|
||||
## [[inputs.mock.step]]
|
||||
## name = "plus_one"
|
||||
## start = 0.0
|
||||
## step = 1.0
|
||||
## [[inputs.mock.stock]]
|
||||
## name = "abc"
|
||||
## price = 50.00
|
||||
## volatility = 0.2
|
||||
```
|
||||
|
||||
## Available Algorithms
|
||||
|
||||
The available algorithms for generating mock data include:
|
||||
|
||||
* Random Float - generate a random float, inclusive of min and max
|
||||
* Sine Wave - produce a sine wave with a certain amplitude and period
|
||||
* Step - always add the step value, negative values accepted
|
||||
* Stock - generate fake, stock-like price values based on a volatility variable
|
||||
|
||||
## Example Output
|
||||
|
||||
The following example shows all available algorithms configured with an
|
||||
additional two tags as well:
|
||||
|
||||
```s
|
||||
mock_sensors,building=5A,site=FTC random=4.875966794516125,abc=50,wave=0,plus_one=0 1632170840000000000
|
||||
mock_sensors,building=5A,site=FTC random=5.738651873834452,abc=45.095549448434774,wave=5.877852522924732,plus_one=1 1632170850000000000
|
||||
mock_sensors,building=5A,site=FTC random=1.0429328917205203,abc=51.928560083072924,wave=9.510565162951535,plus_one=2 1632170860000000000
|
||||
mock_sensors,building=5A,site=FTC random=5.290188595384418,abc=44.41090520217027,wave=9.510565162951536,plus_one=3 1632170870000000000
|
||||
mock_sensors,building=5A,site=FTC random=2.0724967227069135,abc=47.212167806890314,wave=5.877852522924733,plus_one=4 1632170880000000000
|
||||
```
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type Mock struct {
|
||||
counter int64
|
||||
|
||||
MetricName string `toml:"metric_name"`
|
||||
Tags map[string]string `toml:"tags"`
|
||||
|
||||
Random []*random `toml:"random"`
|
||||
Step []*step `toml:"step"`
|
||||
Stock []*stock `toml:"stock"`
|
||||
SineWave []*sineWave `toml:"sine_wave"`
|
||||
}
|
||||
|
||||
type random struct {
|
||||
Name string `toml:"name"`
|
||||
Min float64 `toml:"min"`
|
||||
Max float64 `toml:"max"`
|
||||
}
|
||||
|
||||
type sineWave struct {
|
||||
Name string `toml:"name"`
|
||||
Amplitude float64 `toml:"amplitude"`
|
||||
Period float64 `toml:"period"`
|
||||
}
|
||||
|
||||
type step struct {
|
||||
latest float64
|
||||
|
||||
Name string `toml:"name"`
|
||||
Start float64 `toml:"min"`
|
||||
Step float64 `toml:"max"`
|
||||
}
|
||||
|
||||
type stock struct {
|
||||
latest float64
|
||||
|
||||
Name string `toml:"name"`
|
||||
Price float64 `toml:"price"`
|
||||
Volatility float64 `toml:"volatility"`
|
||||
}
|
||||
|
||||
const sampleConfig = `
|
||||
## Set the metric name to use for reporting
|
||||
metric_name = "mock"
|
||||
|
||||
## Optional string key-value pairs of tags to add to all metrics
|
||||
# [inputs.mock.tags]
|
||||
# "key" = "value"
|
||||
|
||||
## One or more mock data fields *must* be defined.
|
||||
##
|
||||
## [[inputs.mock.random]]
|
||||
## name = "rand"
|
||||
## min = 1.0
|
||||
## max = 6.0
|
||||
## [[inputs.mock.sine_wave]]
|
||||
## name = "wave"
|
||||
## amplitude = 1.0
|
||||
## period = 0.5
|
||||
## [[inputs.mock.step]]
|
||||
## name = "plus_one"
|
||||
## start = 0.0
|
||||
## step = 1.0
|
||||
## [[inputs.mock.stock]]
|
||||
## name = "abc"
|
||||
## price = 50.00
|
||||
## volatility = 0.2
|
||||
`
|
||||
|
||||
func (m *Mock) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (m *Mock) Description() string {
|
||||
return "Generate metrics for test and demonstration purposes"
|
||||
}
|
||||
|
||||
func (m *Mock) Init() error {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mock) Gather(acc telegraf.Accumulator) error {
|
||||
fields := make(map[string]interface{})
|
||||
m.generateRandomFloat64(fields)
|
||||
m.generateStockPrice(fields)
|
||||
m.generateSineWave(fields)
|
||||
m.generateStep(fields)
|
||||
|
||||
tags := make(map[string]string)
|
||||
for key, value := range m.Tags {
|
||||
tags[key] = value
|
||||
}
|
||||
|
||||
acc.AddFields(m.MetricName, fields, tags)
|
||||
|
||||
m.counter++
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate random value between min and max, inclusivly
|
||||
func (m *Mock) generateRandomFloat64(fields map[string]interface{}) {
|
||||
for _, random := range m.Random {
|
||||
fields[random.Name] = random.Min + rand.Float64()*(random.Max-random.Min)
|
||||
}
|
||||
}
|
||||
|
||||
// Create sine waves
|
||||
func (m *Mock) generateSineWave(fields map[string]interface{}) {
|
||||
for _, field := range m.SineWave {
|
||||
fields[field.Name] = math.Sin((float64(m.counter) * field.Period * math.Pi)) * field.Amplitude
|
||||
}
|
||||
}
|
||||
|
||||
// Begin at start value and then add step value every tick
|
||||
func (m *Mock) generateStep(fields map[string]interface{}) {
|
||||
for _, step := range m.Step {
|
||||
if m.counter == 0 {
|
||||
step.latest = step.Start
|
||||
} else {
|
||||
step.latest += step.Step
|
||||
}
|
||||
|
||||
fields[step.Name] = step.latest
|
||||
}
|
||||
}
|
||||
|
||||
// Begin at start price and then generate random value
|
||||
func (m *Mock) generateStockPrice(fields map[string]interface{}) {
|
||||
for _, stock := range m.Stock {
|
||||
if stock.latest == 0.0 {
|
||||
stock.latest = stock.Price
|
||||
} else {
|
||||
noise := 2 * (rand.Float64() - 0.5)
|
||||
stock.latest = stock.latest + (stock.latest * stock.Volatility * noise)
|
||||
|
||||
// avoid going below zero
|
||||
if stock.latest < 1.0 {
|
||||
stock.latest = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
fields[stock.Name] = stock.latest
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("mock", func() telegraf.Input {
|
||||
return &Mock{}
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package mock
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGather(t *testing.T) {
|
||||
testRandom := &random{
|
||||
Name: "random",
|
||||
Min: 1.0,
|
||||
Max: 6.0,
|
||||
}
|
||||
testSineWave := &sineWave{
|
||||
Name: "sine",
|
||||
Amplitude: 1.0,
|
||||
Period: 0.5,
|
||||
}
|
||||
testStep := &step{
|
||||
Name: "step",
|
||||
Start: 0.0,
|
||||
Step: 1.0,
|
||||
}
|
||||
testStock := &stock{
|
||||
Name: "abc",
|
||||
Price: 50.00,
|
||||
Volatility: 0.2,
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"buildling": "tbd",
|
||||
"site": "nowhere",
|
||||
}
|
||||
|
||||
m := &Mock{
|
||||
MetricName: "test",
|
||||
Tags: tags,
|
||||
|
||||
Random: []*random{testRandom},
|
||||
SineWave: []*sineWave{testSineWave},
|
||||
Step: []*step{testStep},
|
||||
Stock: []*stock{testStock},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
require.Len(t, acc.Metrics, 1)
|
||||
|
||||
metric := acc.Metrics[0]
|
||||
require.Equal(t, "test", metric.Measurement)
|
||||
require.Equal(t, tags, metric.Tags)
|
||||
for k, v := range metric.Fields {
|
||||
switch k {
|
||||
case "abc":
|
||||
require.Equal(t, 50.0, v)
|
||||
case "random":
|
||||
require.GreaterOrEqual(t, 6.0, v)
|
||||
require.LessOrEqual(t, 1.0, v)
|
||||
case "sine":
|
||||
require.Equal(t, 0.0, v)
|
||||
case "step":
|
||||
require.Equal(t, 0.0, v)
|
||||
default:
|
||||
require.Failf(t, "unexpected field %q", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatherEmpty(t *testing.T) {
|
||||
m := &Mock{
|
||||
MetricName: "test_empty",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, m.Gather(&acc))
|
||||
|
||||
acc.AssertDoesNotContainMeasurement(t, "test_empty")
|
||||
}
|
||||
Loading…
Reference in New Issue