#2239 - Initial implementation of CW.get_metric_data
This commit is contained in:
parent
66b26cd7b0
commit
fb0de99e81
@ -295,6 +295,43 @@ class CloudWatchBackend(BaseBackend):
|
||||
)
|
||||
)
|
||||
|
||||
def get_metric_data(self, queries, start_time, end_time):
|
||||
period_data = [
|
||||
md for md in self.metric_data if start_time <= md.timestamp <= end_time
|
||||
]
|
||||
results = []
|
||||
for query in queries:
|
||||
query_ns = query["metric_stat._metric._namespace"]
|
||||
query_name = query["metric_stat._metric._metric_name"]
|
||||
query_data = [
|
||||
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 = []
|
||||
stat = query["metric_stat._stat"]
|
||||
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))
|
||||
|
||||
label = query["metric_stat._metric._metric_name"] + " " + stat
|
||||
results.append(
|
||||
{
|
||||
"id": query["id"],
|
||||
"label": label,
|
||||
"vals": result_vals,
|
||||
"timestamps": [datetime.now() for _ in result_vals],
|
||||
}
|
||||
)
|
||||
return results
|
||||
|
||||
def get_metric_statistics(
|
||||
self, namespace, metric_name, start_time, end_time, period, stats
|
||||
):
|
||||
|
@ -92,6 +92,18 @@ class CloudWatchResponse(BaseResponse):
|
||||
template = self.response_template(PUT_METRIC_DATA_TEMPLATE)
|
||||
return template.render()
|
||||
|
||||
@amzn_request_id
|
||||
def get_metric_data(self):
|
||||
start = dtparse(self._get_param("StartTime"))
|
||||
end = dtparse(self._get_param("EndTime"))
|
||||
queries = self._get_list_prefix("MetricDataQueries.member")
|
||||
results = self.cloudwatch_backend.get_metric_data(
|
||||
start_time=start, end_time=end, queries=queries
|
||||
)
|
||||
|
||||
template = self.response_template(GET_METRIC_DATA_TEMPLATE)
|
||||
return template.render(results=results)
|
||||
|
||||
@amzn_request_id
|
||||
def get_metric_statistics(self):
|
||||
namespace = self._get_param("Namespace")
|
||||
@ -285,6 +297,35 @@ PUT_METRIC_DATA_TEMPLATE = """<PutMetricDataResponse xmlns="http://monitoring.am
|
||||
</ResponseMetadata>
|
||||
</PutMetricDataResponse>"""
|
||||
|
||||
GET_METRIC_DATA_TEMPLATE = """<GetMetricDataResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>
|
||||
{{ request_id }}
|
||||
</RequestId>
|
||||
</ResponseMetadata>
|
||||
<GetMetricDataResult>
|
||||
<MetricDataResults>
|
||||
{% for result in results %}
|
||||
<member>
|
||||
<Id>{{ result.id }}</Id>
|
||||
<Label>{{ result.label }}</Label>
|
||||
<StatusCode>Complete</StatusCode>
|
||||
<Timestamps>
|
||||
{% for val in result.timestamps %}
|
||||
<member>{{ val }}</member>
|
||||
{% endfor %}
|
||||
</Timestamps>
|
||||
<Values>
|
||||
{% for val in result.vals %}
|
||||
<member>{{ val }}</member>
|
||||
{% endfor %}
|
||||
</Values>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</MetricDataResults>
|
||||
</GetMetricDataResult>
|
||||
</GetMetricDataResponse>"""
|
||||
|
||||
GET_METRIC_STATISTICS_TEMPLATE = """<GetMetricStatisticsResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>
|
||||
|
@ -3,6 +3,7 @@
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
from datetime import datetime, timedelta
|
||||
from freezegun import freeze_time
|
||||
from nose.tools import assert_raises
|
||||
from uuid import uuid4
|
||||
import pytz
|
||||
@ -211,6 +212,35 @@ def test_get_metric_statistics():
|
||||
datapoint["Sum"].should.equal(1.5)
|
||||
|
||||
|
||||
@mock_cloudwatch
|
||||
@freeze_time("2020-02-10 18:44:05")
|
||||
def test_custom_timestamp():
|
||||
utc_now = datetime.now(tz=pytz.utc)
|
||||
time = "2020-02-10T18:44:09Z"
|
||||
cw = boto3.client("cloudwatch", "eu-west-1")
|
||||
|
||||
cw.put_metric_data(
|
||||
Namespace="tester",
|
||||
MetricData=[dict(MetricName="metric1", Value=1.5, Timestamp=time)],
|
||||
)
|
||||
|
||||
cw.put_metric_data(
|
||||
Namespace="tester",
|
||||
MetricData=[
|
||||
dict(MetricName="metric2", Value=1.5, Timestamp=datetime(2020, 2, 10))
|
||||
],
|
||||
)
|
||||
|
||||
stats = cw.get_metric_statistics(
|
||||
Namespace="tester",
|
||||
MetricName="metric",
|
||||
StartTime=utc_now - timedelta(seconds=60),
|
||||
EndTime=utc_now + timedelta(seconds=60),
|
||||
Period=60,
|
||||
Statistics=["SampleCount", "Sum"],
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudwatch
|
||||
def test_list_metrics():
|
||||
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
||||
@ -292,3 +322,232 @@ def create_metrics(cloudwatch, namespace, metrics=5, data_points=5):
|
||||
Namespace=namespace,
|
||||
MetricData=[{"MetricName": metric_name, "Value": j, "Unit": "Seconds"}],
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudwatch
|
||||
def test_get_metric_data_within_timeframe():
|
||||
utc_now = datetime.now(tz=pytz.utc)
|
||||
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
||||
namespace1 = "my_namespace/"
|
||||
# put metric data
|
||||
values = [0, 2, 4, 3.5, 7, 100]
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace1,
|
||||
MetricData=[
|
||||
{"MetricName": "metric1", "Value": val, "Unit": "Seconds"} for val in values
|
||||
],
|
||||
)
|
||||
# get_metric_data
|
||||
stats = ["Average", "Sum", "Minimum", "Maximum"]
|
||||
response = cloudwatch.get_metric_data(
|
||||
MetricDataQueries=[
|
||||
{
|
||||
"Id": "result_" + stat,
|
||||
"MetricStat": {
|
||||
"Metric": {"Namespace": namespace1, "MetricName": "metric1"},
|
||||
"Period": 60,
|
||||
"Stat": stat,
|
||||
},
|
||||
}
|
||||
for stat in stats
|
||||
],
|
||||
StartTime=utc_now - timedelta(seconds=60),
|
||||
EndTime=utc_now + timedelta(seconds=60),
|
||||
)
|
||||
#
|
||||
# Assert Average/Min/Max/Sum is returned as expected
|
||||
avg = [
|
||||
res for res in response["MetricDataResults"] if res["Id"] == "result_Average"
|
||||
][0]
|
||||
avg["Label"].should.equal("metric1 Average")
|
||||
avg["StatusCode"].should.equal("Complete")
|
||||
[int(val) for val in avg["Values"]].should.equal([19])
|
||||
|
||||
sum_ = [res for res in response["MetricDataResults"] if res["Id"] == "result_Sum"][
|
||||
0
|
||||
]
|
||||
sum_["Label"].should.equal("metric1 Sum")
|
||||
sum_["StatusCode"].should.equal("Complete")
|
||||
[val for val in sum_["Values"]].should.equal([sum(values)])
|
||||
|
||||
min_ = [
|
||||
res for res in response["MetricDataResults"] if res["Id"] == "result_Minimum"
|
||||
][0]
|
||||
min_["Label"].should.equal("metric1 Minimum")
|
||||
min_["StatusCode"].should.equal("Complete")
|
||||
[int(val) for val in min_["Values"]].should.equal([0])
|
||||
|
||||
max_ = [
|
||||
res for res in response["MetricDataResults"] if res["Id"] == "result_Maximum"
|
||||
][0]
|
||||
max_["Label"].should.equal("metric1 Maximum")
|
||||
max_["StatusCode"].should.equal("Complete")
|
||||
[int(val) for val in max_["Values"]].should.equal([100])
|
||||
|
||||
|
||||
@mock_cloudwatch
|
||||
def test_get_metric_data_partially_within_timeframe():
|
||||
utc_now = datetime.now(tz=pytz.utc)
|
||||
yesterday = utc_now - timedelta(days=1)
|
||||
last_week = utc_now - timedelta(days=7)
|
||||
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
||||
namespace1 = "my_namespace/"
|
||||
# put metric data
|
||||
values = [0, 2, 4, 3.5, 7, 100]
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace1,
|
||||
MetricData=[
|
||||
{
|
||||
"MetricName": "metric1",
|
||||
"Value": 10,
|
||||
"Unit": "Seconds",
|
||||
"Timestamp": utc_now,
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace1,
|
||||
MetricData=[
|
||||
{
|
||||
"MetricName": "metric1",
|
||||
"Value": 20,
|
||||
"Unit": "Seconds",
|
||||
"Timestamp": yesterday,
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace1,
|
||||
MetricData=[
|
||||
{
|
||||
"MetricName": "metric1",
|
||||
"Value": 50,
|
||||
"Unit": "Seconds",
|
||||
"Timestamp": last_week,
|
||||
}
|
||||
],
|
||||
)
|
||||
# get_metric_data
|
||||
response = cloudwatch.get_metric_data(
|
||||
MetricDataQueries=[
|
||||
{
|
||||
"Id": "result",
|
||||
"MetricStat": {
|
||||
"Metric": {"Namespace": namespace1, "MetricName": "metric1"},
|
||||
"Period": 60,
|
||||
"Stat": "Sum",
|
||||
},
|
||||
}
|
||||
],
|
||||
StartTime=yesterday - timedelta(seconds=60),
|
||||
EndTime=utc_now + timedelta(seconds=60),
|
||||
)
|
||||
#
|
||||
# Assert Last week's data is not returned
|
||||
len(response["MetricDataResults"]).should.equal(1)
|
||||
sum_ = response["MetricDataResults"][0]
|
||||
sum_["Label"].should.equal("metric1 Sum")
|
||||
sum_["StatusCode"].should.equal("Complete")
|
||||
sum_["Values"].should.equal([30.0])
|
||||
|
||||
|
||||
@mock_cloudwatch
|
||||
def test_get_metric_data_outside_timeframe():
|
||||
utc_now = datetime.now(tz=pytz.utc)
|
||||
last_week = utc_now - timedelta(days=7)
|
||||
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
||||
namespace1 = "my_namespace/"
|
||||
# put metric data
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace1,
|
||||
MetricData=[
|
||||
{
|
||||
"MetricName": "metric1",
|
||||
"Value": 50,
|
||||
"Unit": "Seconds",
|
||||
"Timestamp": last_week,
|
||||
}
|
||||
],
|
||||
)
|
||||
# get_metric_data
|
||||
response = cloudwatch.get_metric_data(
|
||||
MetricDataQueries=[
|
||||
{
|
||||
"Id": "result",
|
||||
"MetricStat": {
|
||||
"Metric": {"Namespace": namespace1, "MetricName": "metric1"},
|
||||
"Period": 60,
|
||||
"Stat": "Sum",
|
||||
},
|
||||
}
|
||||
],
|
||||
StartTime=utc_now - timedelta(seconds=60),
|
||||
EndTime=utc_now + timedelta(seconds=60),
|
||||
)
|
||||
#
|
||||
# Assert Last week's data is not returned
|
||||
len(response["MetricDataResults"]).should.equal(1)
|
||||
response["MetricDataResults"][0]["Id"].should.equal("result")
|
||||
response["MetricDataResults"][0]["StatusCode"].should.equal("Complete")
|
||||
response["MetricDataResults"][0]["Values"].should.equal([])
|
||||
|
||||
|
||||
@mock_cloudwatch
|
||||
def test_get_metric_data_for_multiple_metrics():
|
||||
utc_now = datetime.now(tz=pytz.utc)
|
||||
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
||||
namespace = "my_namespace/"
|
||||
# put metric data
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace,
|
||||
MetricData=[
|
||||
{
|
||||
"MetricName": "metric1",
|
||||
"Value": 50,
|
||||
"Unit": "Seconds",
|
||||
"Timestamp": utc_now,
|
||||
}
|
||||
],
|
||||
)
|
||||
cloudwatch.put_metric_data(
|
||||
Namespace=namespace,
|
||||
MetricData=[
|
||||
{
|
||||
"MetricName": "metric2",
|
||||
"Value": 25,
|
||||
"Unit": "Seconds",
|
||||
"Timestamp": utc_now,
|
||||
}
|
||||
],
|
||||
)
|
||||
# get_metric_data
|
||||
response = cloudwatch.get_metric_data(
|
||||
MetricDataQueries=[
|
||||
{
|
||||
"Id": "result1",
|
||||
"MetricStat": {
|
||||
"Metric": {"Namespace": namespace, "MetricName": "metric1"},
|
||||
"Period": 60,
|
||||
"Stat": "Sum",
|
||||
},
|
||||
},
|
||||
{
|
||||
"Id": "result2",
|
||||
"MetricStat": {
|
||||
"Metric": {"Namespace": namespace, "MetricName": "metric2"},
|
||||
"Period": 60,
|
||||
"Stat": "Sum",
|
||||
},
|
||||
},
|
||||
],
|
||||
StartTime=utc_now - timedelta(seconds=60),
|
||||
EndTime=utc_now + timedelta(seconds=60),
|
||||
)
|
||||
#
|
||||
len(response["MetricDataResults"]).should.equal(2)
|
||||
|
||||
res1 = [res for res in response["MetricDataResults"] if res["Id"] == "result1"][0]
|
||||
res1["Values"].should.equal([50.0])
|
||||
|
||||
res2 = [res for res in response["MetricDataResults"] if res["Id"] == "result2"][0]
|
||||
res2["Values"].should.equal([25.0])
|
||||
|
Loading…
Reference in New Issue
Block a user