import rados
from mgr_module import CommandResult
-
-try:
- from more_itertools import pairwise
-except ImportError:
- def pairwise(iterable):
- from itertools import tee
- a, b = tee(iterable)
- next(b, None)
- return zip(a, b)
+from mgr_util import get_time_series_rates, get_most_recent_rate
from .. import mgr
stats = pool_stats[pool['pool']]
s = {}
- def get_rate(series):
- if len(series) >= 2:
- return differentiate(*list(series)[-2:])
- return 0
-
for stat_name, stat_series in stats.items():
+ rates = get_time_series_rates(stat_series)
s[stat_name] = {
'latest': stat_series[0][1],
- 'rate': get_rate(stat_series),
- 'rates': get_rates_from_data(stat_series)
+ 'rate': get_most_recent_rate(rates),
+ 'rates': rates
}
pool['stats'] = s
pools_w_stats.append(pool)
:return: the derivative of mgr.get_counter()
:rtype: list[tuple[int, float]]"""
data = mgr.get_counter(svc_type, svc_name, path)[path]
- return get_rates_from_data(data)
+ return get_time_series_rates(data)
@classmethod
def get_rate(cls, svc_type, svc_name, path):
"""returns most recent rate"""
- data = mgr.get_counter(svc_type, svc_name, path)[path]
-
- if data and len(data) > 1:
- return differentiate(*data[-2:])
- return 0.0
+ return get_most_recent_rate(cls.get_rates(svc_type, svc_name, path))
@classmethod
def get_client_perf(cls):
'statuses': pg_summary['all'],
'pgs_per_osd': pgs_per_osd,
}
-
-
-def get_rates_from_data(data):
- """
- >>> get_rates_from_data([])
- [(0, 0.0)]
- >>> get_rates_from_data([[1, 42]])
- [(1, 0.0)]
- >>> get_rates_from_data([[0, 100], [2, 101], [3, 100], [4, 100]])
- [(2, 0.5), (3, 1.0), (4, 0.0)]
- """
- if not data:
- return [(0, 0.0)]
- if len(data) == 1:
- return [(data[0][0], 0.0)]
- return [(data2[0], differentiate(data1, data2)) for data1, data2 in pairwise(data)]
-
-
-def differentiate(data1, data2):
- """
- >>> times = [0, 2]
- >>> values = [100, 101]
- >>> differentiate(*zip(times, values))
- 0.5
- >>> times = [0, 2]
- >>> values = [100, 99]
- >>> differentiate(*zip(times, values))
- 0.5
- """
- return abs((data2[1] - data1[1]) / float(data2[0] - data1[0]))
logger.warning(
'Private key {} and certificate {} do not match up: {}'.format(
pkey_fname, cert_fname, str(e)))
+
+def get_most_recent_rate(rates):
+ """ Get most recent rate from rates
+
+ :param rates: The derivative between all time series data points [time in seconds, value]
+ :type rates: list[tuple[int, float]]
+
+ :return: The last derivative or 0.0 if none exists
+ :rtype: float
+
+ >>> get_most_recent_rate(None)
+ 0.0
+ >>> get_most_recent_rate([])
+ 0.0
+ >>> get_most_recent_rate([(1, -2.0)])
+ -2.0
+ >>> get_most_recent_rate([(1, 2.0), (2, 1.5), (3, 5.0)])
+ 5.0
+ """
+ if not rates:
+ return 0.0
+ return rates[-1][1]
+
+def get_time_series_rates(data):
+ """ Rates from time series data
+
+ :param data: Time series data [time in seconds, value]
+ :type data: list[tuple[int, float]]
+
+ :return: The derivative between all time series data points [time in seconds, value]
+ :rtype: list[tuple[int, float]]
+
+ >>> logger.debug = lambda s,x,y: print(s % (x,y))
+ >>> get_time_series_rates([])
+ []
+ >>> get_time_series_rates([[0, 1], [1, 3]])
+ [(1, 2.0)]
+ >>> get_time_series_rates([[0, 2], [0, 3], [0, 1], [1, 2], [1, 3]])
+ Duplicate timestamp in time series data: [0, 2], [0, 3]
+ Duplicate timestamp in time series data: [0, 3], [0, 1]
+ Duplicate timestamp in time series data: [1, 2], [1, 3]
+ [(1, 2.0)]
+ >>> get_time_series_rates([[1, 1], [2, 3], [4, 11], [5, 16], [6, 22]])
+ [(2, 2.0), (4, 4.0), (5, 5.0), (6, 6.0)]
+ """
+ data = _filter_time_series(data)
+ if not data:
+ return []
+ return [(data2[0], _derivative(data1, data2)) for data1, data2 in
+ _pairwise(data)]
+
+def _filter_time_series(data):
+ """ Filters time series data
+
+ Filters out samples with the same timestamp in given time series data.
+ It also enforces the list to contain at least two samples.
+
+ All filtered values will be shown in the debug log. If values were filtered it's a bug in the
+ time series data collector, please report it.
+
+ :param data: Time series data [time in seconds, value]
+ :type data: list[tuple[int, float]]
+
+ :return: Filtered time series data [time in seconds, value]
+ :rtype: list[tuple[int, float]]
+
+ >>> logger.debug = lambda s,x,y: print(s % (x,y))
+ >>> _filter_time_series([])
+ []
+ >>> _filter_time_series([[1, 42]])
+ []
+ >>> _filter_time_series([[10, 2], [10, 3]])
+ Duplicate timestamp in time series data: [10, 2], [10, 3]
+ []
+ >>> _filter_time_series([[0, 1], [1, 2]])
+ [[0, 1], [1, 2]]
+ >>> _filter_time_series([[0, 2], [0, 3], [0, 1], [1, 2], [1, 3]])
+ Duplicate timestamp in time series data: [0, 2], [0, 3]
+ Duplicate timestamp in time series data: [0, 3], [0, 1]
+ Duplicate timestamp in time series data: [1, 2], [1, 3]
+ [[0, 1], [1, 3]]
+ >>> _filter_time_series([[1, 1], [2, 3], [4, 11], [5, 16], [6, 22]])
+ [[1, 1], [2, 3], [4, 11], [5, 16], [6, 22]]
+ """
+ filtered = []
+ for i in range(len(data) - 1):
+ if data[i][0] == data[i + 1][0]: # Same timestamp
+ logger.debug("Duplicate timestamp in time series data: %s, %s", data[i], data[i + 1])
+ continue
+ filtered.append(data[i])
+ if not filtered:
+ return []
+ filtered.append(data[-1])
+ return filtered
+
+def _derivative(p1, p2):
+ """ Derivative between two time series data points
+
+ :param p1: Time series data [time in seconds, value]
+ :type p1: tuple[int, float]
+ :param p2: Time series data [time in seconds, value]
+ :type p2: tuple[int, float]
+
+ :return: Derivative between both points
+ :rtype: float
+
+ >>> _derivative([0, 0], [2, 1])
+ 0.5
+ >>> _derivative([0, 1], [2, 0])
+ -0.5
+ >>> _derivative([0, 0], [3, 1])
+ 0.3333333333333333
+ """
+ return (p2[1] - p1[1]) / float(p2[0] - p1[0])
+
+def _pairwise(iterable):
+ it = iter(iterable)
+ a = next(it, None)
+
+ for b in it:
+ yield (a, b)
+ a = b