Module: check_mk
Branch: master
Commit: 67759e66b5edef4b7006fc3bc6a8dcd3376bd135
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=67759e66b5edef…
Author: Óscar Nájera <on(a)mathias-kettner.de>
Date: Tue Apr 2 16:19:33 2019 +0200
New TimeSeries class to describe RRDdata
This class allows to track performance data and binds to each value the
measurement time stamp.
It allows for up-sampling data by backward filling values
CMK-1761
Change-Id: I81646be8f009cd1bbd4efec0ca9c298abe002c4d
---
cmk/utils/prediction.py | 60 +++++++++++++++++++++++++
tests/unit/cmk/utils/test_prediction_commons.py | 20 +++++++++
2 files changed, 80 insertions(+)
diff --git a/cmk/utils/prediction.py b/cmk/utils/prediction.py
index cdd1b4c..4c79d3e 100644
--- a/cmk/utils/prediction.py
+++ b/cmk/utils/prediction.py
@@ -51,6 +51,66 @@ def timezone_at(timestamp):
return time.timezone
+def rrd_timestamps(twindow):
+ start, end, step = twindow
+ if step == 0:
+ return []
+ return [t + step for t in range(start, end, step)]
+
+
+class TimeSeries(object):
+ """Describes the returned time series returned by livestatus
+
+ - Timestamped values are valid for the measurement interval:
+ [timestamp-step; timestamp[
+ which means they are at the end of the interval.
+ - The Series describes the interval [start; end[
+ - Start has no associated value to it.
+ """
+
+ def __init__(self, data):
+ self.start = data[0]
+ self.end = data[1]
+ self.step = data[2]
+ self.values = data[3:]
+
+ @property
+ def twindow(self):
+ return self.start, self.end, self.step
+
+ def bfill_upsample(self, twindow, shift):
+ """Upsample by backward filling values
+
+ twindow : 3-tuple, (start, end, step)
+ description of target time interval
+ """
+ upsa = []
+ i = 0
+ start, end, step = twindow
+ current_times = rrd_timestamps(self.twindow)
+ if start != self.start or end != self.end or step != self.step:
+ for t in range(start, end, step):
+ if t >= current_times[i] + shift:
+ i += 1
+ upsa.append(self.values[i])
+
+ return upsa
+
+ return self.values
+
+ def time_data_pairs(self):
+ return list(zip(rrd_timestamps(self.twindow), self.values))
+
+ def __repr__(self):
+ return str(list(self.twindow) + self.values)
+
+ def __eq__(self, other):
+ if not isinstance(other, TimeSeries):
+ return NotImplemented
+
+ return self.start == other.start and self.end == other.end and self.step ==
other.step and self.values == other.values
+
+
def get_rrd_data(hostname, service_description, varname, cf, fromtime, untiltime):
"""Fetch RRD historic metrics data of a specific service, within the
specified time range
diff --git a/tests/unit/cmk/utils/test_prediction_commons.py
b/tests/unit/cmk/utils/test_prediction_commons.py
index f538c3c..b15ed07 100644
--- a/tests/unit/cmk/utils/test_prediction_commons.py
+++ b/tests/unit/cmk/utils/test_prediction_commons.py
@@ -3,6 +3,26 @@ import pytest
import cmk.utils.prediction as prediction
+(a)pytest.mark.parametrize("twindowindow, result", [((0, 0, 0), []),
+ ((100, 200, 25), [125, 150, 175, 200])])
+def test_rrdtimestamps(twindow, result):
+ assert prediction.rrd_timestamps(twindow) == result
+
+
+(a)pytest.mark.parametrize("rrddataddata, twindow, shift, upsampled", [
+ ([10, 20, 10, 20], (10, 20, 10), 0, [20]),
+ ([10, 20, 10, 20], (10, 20, 5), 0, [20, 20]),
+ ([10, 20, 10, 20], (20, 30, 5), 10, [20, 20]),
+ ([0, 120, 40, 25, 65, 105], (300, 400, 10), 300, [25, 25, 25, 25, 65, 65, 65, 65,
105, 105]),
+ ([0, 120, 40, 25, None, 105],
+ (300, 400, 10), 300, [25, 25, 25, 25, None, None, None, None, 105, 105]),
+ ([0, 120, 40, 25, 65, 105], (330, 410, 10), 300, [25, 65, 65, 65, 65, 105, 105,
105]),
+])
+def test_time_series_upsampling(rrddata, twindow, shift, upsampled):
+ ts = prediction.TimeSeries(rrddata)
+ assert ts.bfill_upsample(twindow, shift) == upsampled
+
+
@pytest.mark.parametrize("ref_value, stdev, sig, params, levels_factor,
result", [
(2, 0.5, 1, ("absolute", (3, 5)), 0.5, (3.5, 4.5)),
(2, 0.5, -1, ("relative", (20, 50)), 0.5, (1.6, 1)),