#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(
|
def get_metric_statistics(
|
||||||
self, namespace, metric_name, start_time, end_time, period, stats
|
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)
|
template = self.response_template(PUT_METRIC_DATA_TEMPLATE)
|
||||||
return template.render()
|
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
|
@amzn_request_id
|
||||||
def get_metric_statistics(self):
|
def get_metric_statistics(self):
|
||||||
namespace = self._get_param("Namespace")
|
namespace = self._get_param("Namespace")
|
||||||
@ -285,6 +297,35 @@ PUT_METRIC_DATA_TEMPLATE = """<PutMetricDataResponse xmlns="http://monitoring.am
|
|||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</PutMetricDataResponse>"""
|
</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/">
|
GET_METRIC_STATISTICS_TEMPLATE = """<GetMetricStatisticsResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>
|
<RequestId>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import boto3
|
import boto3
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from freezegun import freeze_time
|
||||||
from nose.tools import assert_raises
|
from nose.tools import assert_raises
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import pytz
|
import pytz
|
||||||
@ -211,6 +212,35 @@ def test_get_metric_statistics():
|
|||||||
datapoint["Sum"].should.equal(1.5)
|
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
|
@mock_cloudwatch
|
||||||
def test_list_metrics():
|
def test_list_metrics():
|
||||||
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
cloudwatch = boto3.client("cloudwatch", "eu-west-1")
|
||||||
@ -292,3 +322,232 @@ def create_metrics(cloudwatch, namespace, metrics=5, data_points=5):
|
|||||||
Namespace=namespace,
|
Namespace=namespace,
|
||||||
MetricData=[{"MetricName": metric_name, "Value": j, "Unit": "Seconds"}],
|
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