Cloudwatch get metric data return by period (#3626)

* Cloudwatch get metric data return by period

- addresses https://github.com/localstack/localstack/issues/3493

* fix lint issues

* remove unused import

* added test cases for min max and average
This commit is contained in:
Rahul Ranjan 2021-02-02 14:38:53 +05:30 committed by GitHub
parent d382731a14
commit 78a5661093
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 119 additions and 35 deletions

View File

@ -388,39 +388,62 @@ class CloudWatchBackend(BaseBackend):
) )
) )
def get_metric_data(self, queries, start_time, end_time): def get_metric_data(
self, queries, start_time, end_time, scan_by="TimestampAscending"
):
period_data = [ period_data = [
md for md in self.metric_data if start_time <= md.timestamp <= end_time md for md in self.metric_data if start_time <= md.timestamp <= end_time
] ]
results = [] results = []
for query in queries: for query in queries:
period_start_time = start_time
query_ns = query["metric_stat._metric._namespace"] query_ns = query["metric_stat._metric._namespace"]
query_name = query["metric_stat._metric._metric_name"] query_name = query["metric_stat._metric._metric_name"]
query_data = [ delta = timedelta(seconds=int(query["metric_stat._period"]))
md
for md in period_data
if md.namespace == query_ns and md.name == query_name
]
metric_values = [m.value for m in query_data]
result_vals = [] result_vals = []
timestamps = []
stat = query["metric_stat._stat"] stat = query["metric_stat._stat"]
if len(metric_values) > 0: while period_start_time <= end_time:
if stat == "Average": period_end_time = period_start_time + delta
result_vals.append(sum(metric_values) / len(metric_values)) period_md = [
elif stat == "Minimum": period_md
result_vals.append(min(metric_values)) for period_md in period_data
elif stat == "Maximum": if period_start_time <= period_md.timestamp < period_end_time
result_vals.append(max(metric_values)) ]
elif stat == "Sum":
result_vals.append(sum(metric_values))
query_period_data = [
md
for md in period_md
if md.namespace == query_ns and md.name == query_name
]
metric_values = [m.value for m in query_period_data]
if len(metric_values) > 0:
if stat == "Average":
result_vals.append(sum(metric_values) / len(metric_values))
elif stat == "Minimum":
result_vals.append(min(metric_values))
elif stat == "Maximum":
result_vals.append(max(metric_values))
elif stat == "Sum":
result_vals.append(sum(metric_values))
timestamps.append(
iso_8601_datetime_without_milliseconds(period_start_time)
)
period_start_time += delta
if scan_by == "TimestampDescending" and len(timestamps) > 0:
timestamps.reverse()
result_vals.reverse()
label = query["metric_stat._metric._metric_name"] + " " + stat label = query["metric_stat._metric._metric_name"] + " " + stat
results.append( results.append(
{ {
"id": query["id"], "id": query["id"],
"label": label, "label": label,
"vals": result_vals, "vals": result_vals,
"timestamps": [datetime.now() for _ in result_vals], "timestamps": timestamps,
} }
) )
return results return results

View File

@ -128,9 +128,11 @@ class CloudWatchResponse(BaseResponse):
def get_metric_data(self): def get_metric_data(self):
start = dtparse(self._get_param("StartTime")) start = dtparse(self._get_param("StartTime"))
end = dtparse(self._get_param("EndTime")) end = dtparse(self._get_param("EndTime"))
scan_by = self._get_param("ScanBy")
queries = self._get_list_prefix("MetricDataQueries.member") queries = self._get_list_prefix("MetricDataQueries.member")
results = self.cloudwatch_backend.get_metric_data( results = self.cloudwatch_backend.get_metric_data(
start_time=start, end_time=end, queries=queries start_time=start, end_time=end, queries=queries, scan_by=scan_by
) )
template = self.response_template(GET_METRIC_DATA_TEMPLATE) template = self.response_template(GET_METRIC_DATA_TEMPLATE)

View File

@ -678,6 +678,7 @@ def test_get_metric_data_partially_within_timeframe():
} }
], ],
) )
cloudwatch.put_metric_data( cloudwatch.put_metric_data(
Namespace=namespace1, Namespace=namespace1,
MetricData=[ MetricData=[
@ -686,31 +687,89 @@ def test_get_metric_data_partially_within_timeframe():
"Value": 50, "Value": 50,
"Unit": "Seconds", "Unit": "Seconds",
"Timestamp": last_week, "Timestamp": last_week,
} },
],
)
# get_metric_data
response = cloudwatch.get_metric_data(
MetricDataQueries=[
{ {
"Id": "result", "MetricName": "metric1",
"MetricStat": { "Value": 10,
"Metric": {"Namespace": namespace1, "MetricName": "metric1"}, "Unit": "Seconds",
"Period": 60, "Timestamp": last_week + timedelta(seconds=10),
"Stat": "Sum", },
}, {
} "MetricName": "metric1",
"Value": 20,
"Unit": "Seconds",
"Timestamp": last_week + timedelta(seconds=15),
},
{
"MetricName": "metric1",
"Value": 40,
"Unit": "Seconds",
"Timestamp": last_week + timedelta(seconds=30),
},
], ],
StartTime=yesterday - timedelta(seconds=60),
EndTime=utc_now + timedelta(seconds=60),
) )
#
# data for average, min, max
def get_data(start, end, stat="Sum", scanBy="TimestampAscending"):
# get_metric_data
response = cloudwatch.get_metric_data(
MetricDataQueries=[
{
"Id": "result",
"MetricStat": {
"Metric": {"Namespace": namespace1, "MetricName": "metric1"},
"Period": 60,
"Stat": stat,
},
}
],
StartTime=start,
EndTime=end,
ScanBy=scanBy,
)
return response
response = get_data(
start=yesterday - timedelta(seconds=60), end=utc_now + timedelta(seconds=60),
)
# Assert Last week's data is not returned # Assert Last week's data is not returned
len(response["MetricDataResults"]).should.equal(1) len(response["MetricDataResults"]).should.equal(1)
sum_ = response["MetricDataResults"][0] sum_ = response["MetricDataResults"][0]
sum_["Label"].should.equal("metric1 Sum") sum_["Label"].should.equal("metric1 Sum")
sum_["StatusCode"].should.equal("Complete") sum_["StatusCode"].should.equal("Complete")
sum_["Values"].should.equal([30.0]) sum_["Values"].should.equal([20.0, 10.0])
response = get_data(
start=yesterday - timedelta(seconds=60),
end=utc_now + timedelta(seconds=60),
scanBy="TimestampDescending",
)
response["MetricDataResults"][0]["Values"].should.equal([10.0, 20.0])
response = get_data(
start=last_week - timedelta(seconds=1),
end=utc_now + timedelta(seconds=60),
stat="Average",
)
# assert average
response["MetricDataResults"][0]["Values"].should.equal([30.0, 20.0, 10.0])
response = get_data(
start=last_week - timedelta(seconds=1),
end=utc_now + timedelta(seconds=60),
stat="Maximum",
)
# assert maximum
response["MetricDataResults"][0]["Values"].should.equal([50.0, 20.0, 10.0])
response = get_data(
start=last_week - timedelta(seconds=1),
end=utc_now + timedelta(seconds=60),
stat="Minimum",
)
# assert minimum
response["MetricDataResults"][0]["Values"].should.equal([10.0, 20.0, 10.0])
@mock_cloudwatch @mock_cloudwatch