package main import ( "math" "time" ) // Synthetic traffic shapes for `nmon devtools backfill`. Everything here is a // pure function of its inputs, so a back-fill is reproducible and testable. const ( patternDiurnal = "diurnal" patternFlat = "flat" ) // splitmix64 is a tiny deterministic hash used for noise; math/rand would make // two runs differ and a global source would be shared state. func splitmix64(x uint64) uint64 { x += 0x9e3779b97f4a7c15 x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9 x = (x ^ (x >> 27)) * 0x94d049bb133111eb return x ^ (x >> 31) } // unit maps a hash to [0, 1). func unit(h uint64) float64 { return float64(h>>11) / (1 << 53) } // rateFactor is the multiple of the mean rate at t: 1 for "flat"; for // "diurnal" an evening peak (about 20:00 UTC), an early-morning trough, quieter // weekends, per-sample noise and the odd short burst. func rateFactor(pattern string, t time.Time, salt uint64) float64 { if pattern == patternFlat { return 1 } h := float64(t.Hour()) + float64(t.Minute())/60 f := 1 + 0.6*math.Cos(2*math.Pi*(h-20)/24) if wd := t.Weekday(); wd == time.Saturday || wd == time.Sunday { f *= 0.8 } slot := uint64(t.Unix() / 60) f *= 0.88 + 0.24*unit(splitmix64(salt^slot)) if unit(splitmix64(salt^(slot/4)^0xb5ad4eceda1ce2a9)) < 0.01 { // ~1% of 4-minute slots burst f *= 2.5 } return math.Max(f, 0.05) } // counterSeries returns n cumulative octet-counter values, one per step from // start, beginning at 0 and never decreasing. mean is bytes per second. func counterSeries(pattern string, start time.Time, n int, step time.Duration, mean float64, salt uint64) []float64 { out := make([]float64, n) var total float64 for i := 1; i < n; i++ { t := start.Add(time.Duration(i) * step) total += mean * rateFactor(pattern, t, salt) * step.Seconds() out[i] = math.Floor(total) } return out } // defaultMeanBytesPerSec is a stable per-interface mean (2 to 60 Mbit/s) for when // the live rate is unknown. func defaultMeanBytesPerSec(iface string, salt uint64) float64 { h := salt for _, c := range []byte(iface) { h = splitmix64(h ^ uint64(c)) } return (2 + 58*unit(h)*unit(h>>7)) * 125_000 }