Added SetAlarmState and added state filter to describe
This commit is contained in:
parent
2b9f19ef77
commit
6adfb97753
@ -1,4 +1,7 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
from moto.core import BaseBackend, BaseModel
|
from moto.core import BaseBackend, BaseModel
|
||||||
|
from moto.core.exceptions import RESTError
|
||||||
import boto.ec2.cloudwatch
|
import boto.ec2.cloudwatch
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -35,9 +38,26 @@ class FakeAlarm(BaseModel):
|
|||||||
self.ok_actions = ok_actions
|
self.ok_actions = ok_actions
|
||||||
self.insufficient_data_actions = insufficient_data_actions
|
self.insufficient_data_actions = insufficient_data_actions
|
||||||
self.unit = unit
|
self.unit = unit
|
||||||
self.state_updated_timestamp = datetime.datetime.utcnow()
|
|
||||||
self.configuration_updated_timestamp = datetime.datetime.utcnow()
|
self.configuration_updated_timestamp = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
self.history = []
|
||||||
|
|
||||||
|
self.state_reason = ''
|
||||||
|
self.state_reason_data = '{}'
|
||||||
|
self.state = 'OK'
|
||||||
|
self.state_updated_timestamp = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
def update_state(self, reason, reason_data, state_value):
|
||||||
|
# History type, that then decides what the rest of the items are, can be one of ConfigurationUpdate | StateUpdate | Action
|
||||||
|
self.history.append(
|
||||||
|
('StateUpdate', self.state_reason, self.state_reason_data, self.state, self.state_updated_timestamp)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.state_reason = reason
|
||||||
|
self.state_reason_data = reason_data
|
||||||
|
self.state = state_value
|
||||||
|
self.state_updated_timestamp = datetime.datetime.utcnow()
|
||||||
|
|
||||||
|
|
||||||
class MetricDatum(BaseModel):
|
class MetricDatum(BaseModel):
|
||||||
|
|
||||||
@ -122,10 +142,8 @@ class CloudWatchBackend(BaseBackend):
|
|||||||
if alarm.name in alarm_names
|
if alarm.name in alarm_names
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_alarms_by_state_value(self, state):
|
def get_alarms_by_state_value(self, target_state):
|
||||||
raise NotImplementedError(
|
return filter(lambda alarm: alarm.state == target_state, self.alarms.values())
|
||||||
"DescribeAlarm by state is not implemented in moto."
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete_alarms(self, alarm_names):
|
def delete_alarms(self, alarm_names):
|
||||||
for alarm_name in alarm_names:
|
for alarm_name in alarm_names:
|
||||||
@ -164,6 +182,21 @@ class CloudWatchBackend(BaseBackend):
|
|||||||
def get_dashboard(self, dashboard):
|
def get_dashboard(self, dashboard):
|
||||||
return self.dashboards.get(dashboard)
|
return self.dashboards.get(dashboard)
|
||||||
|
|
||||||
|
def set_alarm_state(self, alarm_name, reason, reason_data, state_value):
|
||||||
|
try:
|
||||||
|
if reason_data is not None:
|
||||||
|
json.loads(reason_data)
|
||||||
|
except ValueError:
|
||||||
|
raise RESTError('InvalidFormat', 'StateReasonData is invalid JSON')
|
||||||
|
|
||||||
|
if alarm_name not in self.alarms:
|
||||||
|
raise RESTError('ResourceNotFound', 'Alarm {0} not found'.format(alarm_name), status=404)
|
||||||
|
|
||||||
|
if state_value not in ('OK', 'ALARM', 'INSUFFICIENT_DATA'):
|
||||||
|
raise RESTError('InvalidParameterValue', 'StateValue is not one of OK | ALARM | INSUFFICIENT_DATA')
|
||||||
|
|
||||||
|
self.alarms[alarm_name].update_state(reason, reason_data, state_value)
|
||||||
|
|
||||||
|
|
||||||
class LogGroup(BaseModel):
|
class LogGroup(BaseModel):
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import json
|
import json
|
||||||
|
from moto.core.utils import amzn_request_id
|
||||||
from moto.core.responses import BaseResponse
|
from moto.core.responses import BaseResponse
|
||||||
from .models import cloudwatch_backends
|
from .models import cloudwatch_backends
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CloudWatchResponse(BaseResponse):
|
class CloudWatchResponse(BaseResponse):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -13,6 +15,7 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(ERROR_RESPONSE_TEMPLATE)
|
template = self.response_template(ERROR_RESPONSE_TEMPLATE)
|
||||||
return template.render(code=code, message=message), dict(status=status)
|
return template.render(code=code, message=message), dict(status=status)
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def put_metric_alarm(self):
|
def put_metric_alarm(self):
|
||||||
name = self._get_param('AlarmName')
|
name = self._get_param('AlarmName')
|
||||||
namespace = self._get_param('Namespace')
|
namespace = self._get_param('Namespace')
|
||||||
@ -40,6 +43,7 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(PUT_METRIC_ALARM_TEMPLATE)
|
template = self.response_template(PUT_METRIC_ALARM_TEMPLATE)
|
||||||
return template.render(alarm=alarm)
|
return template.render(alarm=alarm)
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def describe_alarms(self):
|
def describe_alarms(self):
|
||||||
action_prefix = self._get_param('ActionPrefix')
|
action_prefix = self._get_param('ActionPrefix')
|
||||||
alarm_name_prefix = self._get_param('AlarmNamePrefix')
|
alarm_name_prefix = self._get_param('AlarmNamePrefix')
|
||||||
@ -62,12 +66,14 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(DESCRIBE_ALARMS_TEMPLATE)
|
template = self.response_template(DESCRIBE_ALARMS_TEMPLATE)
|
||||||
return template.render(alarms=alarms)
|
return template.render(alarms=alarms)
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def delete_alarms(self):
|
def delete_alarms(self):
|
||||||
alarm_names = self._get_multi_param('AlarmNames.member')
|
alarm_names = self._get_multi_param('AlarmNames.member')
|
||||||
self.cloudwatch_backend.delete_alarms(alarm_names)
|
self.cloudwatch_backend.delete_alarms(alarm_names)
|
||||||
template = self.response_template(DELETE_METRIC_ALARMS_TEMPLATE)
|
template = self.response_template(DELETE_METRIC_ALARMS_TEMPLATE)
|
||||||
return template.render()
|
return template.render()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def put_metric_data(self):
|
def put_metric_data(self):
|
||||||
namespace = self._get_param('Namespace')
|
namespace = self._get_param('Namespace')
|
||||||
metric_data = []
|
metric_data = []
|
||||||
@ -99,11 +105,13 @@ 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 list_metrics(self):
|
def list_metrics(self):
|
||||||
metrics = self.cloudwatch_backend.get_all_metrics()
|
metrics = self.cloudwatch_backend.get_all_metrics()
|
||||||
template = self.response_template(LIST_METRICS_TEMPLATE)
|
template = self.response_template(LIST_METRICS_TEMPLATE)
|
||||||
return template.render(metrics=metrics)
|
return template.render(metrics=metrics)
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def delete_dashboards(self):
|
def delete_dashboards(self):
|
||||||
dashboards = self._get_multi_param('DashboardNames.member')
|
dashboards = self._get_multi_param('DashboardNames.member')
|
||||||
if dashboards is None:
|
if dashboards is None:
|
||||||
@ -116,18 +124,23 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(DELETE_DASHBOARD_TEMPLATE)
|
template = self.response_template(DELETE_DASHBOARD_TEMPLATE)
|
||||||
return template.render()
|
return template.render()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def describe_alarm_history(self):
|
def describe_alarm_history(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def describe_alarms_for_metric(self):
|
def describe_alarms_for_metric(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def disable_alarm_actions(self):
|
def disable_alarm_actions(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def enable_alarm_actions(self):
|
def enable_alarm_actions(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def get_dashboard(self):
|
def get_dashboard(self):
|
||||||
dashboard_name = self._get_param('DashboardName')
|
dashboard_name = self._get_param('DashboardName')
|
||||||
|
|
||||||
@ -138,9 +151,11 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(GET_DASHBOARD_TEMPLATE)
|
template = self.response_template(GET_DASHBOARD_TEMPLATE)
|
||||||
return template.render(dashboard=dashboard)
|
return template.render(dashboard=dashboard)
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def get_metric_statistics(self):
|
def get_metric_statistics(self):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def list_dashboards(self):
|
def list_dashboards(self):
|
||||||
prefix = self._get_param('DashboardNamePrefix', '')
|
prefix = self._get_param('DashboardNamePrefix', '')
|
||||||
|
|
||||||
@ -149,6 +164,7 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(LIST_DASHBOARD_RESPONSE)
|
template = self.response_template(LIST_DASHBOARD_RESPONSE)
|
||||||
return template.render(dashboards=dashboards)
|
return template.render(dashboards=dashboards)
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def put_dashboard(self):
|
def put_dashboard(self):
|
||||||
name = self._get_param('DashboardName')
|
name = self._get_param('DashboardName')
|
||||||
body = self._get_param('DashboardBody')
|
body = self._get_param('DashboardBody')
|
||||||
@ -163,14 +179,23 @@ class CloudWatchResponse(BaseResponse):
|
|||||||
template = self.response_template(PUT_DASHBOARD_RESPONSE)
|
template = self.response_template(PUT_DASHBOARD_RESPONSE)
|
||||||
return template.render()
|
return template.render()
|
||||||
|
|
||||||
|
@amzn_request_id
|
||||||
def set_alarm_state(self):
|
def set_alarm_state(self):
|
||||||
raise NotImplementedError()
|
alarm_name = self._get_param('AlarmName')
|
||||||
|
reason = self._get_param('StateReason')
|
||||||
|
reason_data = self._get_param('StateReasonData')
|
||||||
|
state_value = self._get_param('StateValue')
|
||||||
|
|
||||||
|
self.cloudwatch_backend.set_alarm_state(alarm_name, reason, reason_data, state_value)
|
||||||
|
|
||||||
|
template = self.response_template(SET_ALARM_STATE_TEMPLATE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
|
|
||||||
PUT_METRIC_ALARM_TEMPLATE = """<PutMetricAlarmResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
PUT_METRIC_ALARM_TEMPLATE = """<PutMetricAlarmResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>
|
<RequestId>
|
||||||
2690d7eb-ed86-11dd-9877-6fad448a8419
|
{{ request_id }}
|
||||||
</RequestId>
|
</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</PutMetricAlarmResponse>"""
|
</PutMetricAlarmResponse>"""
|
||||||
@ -229,7 +254,7 @@ DESCRIBE_ALARMS_TEMPLATE = """<DescribeAlarmsResponse xmlns="http://monitoring.a
|
|||||||
DELETE_METRIC_ALARMS_TEMPLATE = """<DeleteMetricAlarmResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
DELETE_METRIC_ALARMS_TEMPLATE = """<DeleteMetricAlarmResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>
|
<RequestId>
|
||||||
2690d7eb-ed86-11dd-9877-6fad448a8419
|
{{ request_id }}
|
||||||
</RequestId>
|
</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</DeleteMetricAlarmResponse>"""
|
</DeleteMetricAlarmResponse>"""
|
||||||
@ -237,7 +262,7 @@ DELETE_METRIC_ALARMS_TEMPLATE = """<DeleteMetricAlarmResponse xmlns="http://moni
|
|||||||
PUT_METRIC_DATA_TEMPLATE = """<PutMetricDataResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
PUT_METRIC_DATA_TEMPLATE = """<PutMetricDataResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>
|
<RequestId>
|
||||||
2690d7eb-ed86-11dd-9877-6fad448a8419
|
{{ request_id }}
|
||||||
</RequestId>
|
</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</PutMetricDataResponse>"""
|
</PutMetricDataResponse>"""
|
||||||
@ -271,7 +296,7 @@ PUT_DASHBOARD_RESPONSE = """<PutDashboardResponse xmlns="http://monitoring.amazo
|
|||||||
<DashboardValidationMessages/>
|
<DashboardValidationMessages/>
|
||||||
</PutDashboardResult>
|
</PutDashboardResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>44b1d4d8-9fa3-11e7-8ad3-41b86ac5e49e</RequestId>
|
<RequestId>{{ request_id }}</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</PutDashboardResponse>"""
|
</PutDashboardResponse>"""
|
||||||
|
|
||||||
@ -289,14 +314,14 @@ LIST_DASHBOARD_RESPONSE = """<ListDashboardsResponse xmlns="http://monitoring.am
|
|||||||
</DashboardEntries>
|
</DashboardEntries>
|
||||||
</ListDashboardsResult>
|
</ListDashboardsResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>c3773873-9fa5-11e7-b315-31fcc9275d62</RequestId>
|
<RequestId>{{ request_id }}</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</ListDashboardsResponse>"""
|
</ListDashboardsResponse>"""
|
||||||
|
|
||||||
DELETE_DASHBOARD_TEMPLATE = """<DeleteDashboardsResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
DELETE_DASHBOARD_TEMPLATE = """<DeleteDashboardsResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
<DeleteDashboardsResult/>
|
<DeleteDashboardsResult/>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>68d1dc8c-9faa-11e7-a694-df2715690df2</RequestId>
|
<RequestId>{{ request_id }}</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</DeleteDashboardsResponse>"""
|
</DeleteDashboardsResponse>"""
|
||||||
|
|
||||||
@ -307,16 +332,22 @@ GET_DASHBOARD_TEMPLATE = """<GetDashboardResponse xmlns="http://monitoring.amazo
|
|||||||
<DashboardName>{{ dashboard.name }}</DashboardName>
|
<DashboardName>{{ dashboard.name }}</DashboardName>
|
||||||
</GetDashboardResult>
|
</GetDashboardResult>
|
||||||
<ResponseMetadata>
|
<ResponseMetadata>
|
||||||
<RequestId>e3c16bb0-9faa-11e7-b315-31fcc9275d62</RequestId>
|
<RequestId>{{ request_id }}</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</GetDashboardResponse>
|
</GetDashboardResponse>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
SET_ALARM_STATE_TEMPLATE = """<SetAlarmStateResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>{{ request_id }}</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</SetAlarmStateResponse>"""
|
||||||
|
|
||||||
ERROR_RESPONSE_TEMPLATE = """<ErrorResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
ERROR_RESPONSE_TEMPLATE = """<ErrorResponse xmlns="http://monitoring.amazonaws.com/doc/2010-08-01/">
|
||||||
<Error>
|
<Error>
|
||||||
<Type>Sender</Type>
|
<Type>Sender</Type>
|
||||||
<Code>{{ code }}</Code>
|
<Code>{{ code }}</Code>
|
||||||
<Message>{{ message }}</Message>
|
<Message>{{ message }}</Message>
|
||||||
</Error>
|
</Error>
|
||||||
<RequestId>5e45fd1e-9fa3-11e7-b720-89e8821d38c4</RequestId>
|
<RequestId>{{ request_id }}</RequestId>
|
||||||
</ErrorResponse>"""
|
</ErrorResponse>"""
|
||||||
|
@ -272,9 +272,6 @@ def amzn_request_id(f):
|
|||||||
else:
|
else:
|
||||||
status, new_headers, body = response
|
status, new_headers, body = response
|
||||||
headers.update(new_headers)
|
headers.update(new_headers)
|
||||||
# Cast status to string
|
|
||||||
if "status" in headers:
|
|
||||||
headers['status'] = str(headers['status'])
|
|
||||||
|
|
||||||
request_id = gen_amzn_requestid_long(headers)
|
request_id = gen_amzn_requestid_long(headers)
|
||||||
|
|
||||||
|
@ -118,12 +118,3 @@ def test_describe_alarms():
|
|||||||
|
|
||||||
alarms = conn.describe_alarms()
|
alarms = conn.describe_alarms()
|
||||||
alarms.should.have.length_of(0)
|
alarms.should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
@mock_cloudwatch_deprecated
|
|
||||||
def test_describe_state_value_unimplemented():
|
|
||||||
conn = boto.connect_cloudwatch()
|
|
||||||
|
|
||||||
conn.describe_alarms()
|
|
||||||
conn.describe_alarms.when.called_with(
|
|
||||||
state_value="foo").should.throw(NotImplementedError)
|
|
||||||
|
@ -87,6 +87,54 @@ def test_get_dashboard_fail():
|
|||||||
raise RuntimeError('Should of raised error')
|
raise RuntimeError('Should of raised error')
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cloudwatch
|
||||||
|
def test_alarm_state():
|
||||||
|
client = boto3.client('cloudwatch', region_name='eu-central-1')
|
||||||
|
|
||||||
|
client.put_metric_alarm(
|
||||||
|
AlarmName='testalarm1',
|
||||||
|
MetricName='cpu',
|
||||||
|
Namespace='blah',
|
||||||
|
Period=10,
|
||||||
|
EvaluationPeriods=5,
|
||||||
|
Statistic='Average',
|
||||||
|
Threshold=2,
|
||||||
|
ComparisonOperator='GreaterThanThreshold',
|
||||||
|
)
|
||||||
|
client.put_metric_alarm(
|
||||||
|
AlarmName='testalarm2',
|
||||||
|
MetricName='cpu',
|
||||||
|
Namespace='blah',
|
||||||
|
Period=10,
|
||||||
|
EvaluationPeriods=5,
|
||||||
|
Statistic='Average',
|
||||||
|
Threshold=2,
|
||||||
|
ComparisonOperator='GreaterThanThreshold',
|
||||||
|
)
|
||||||
|
|
||||||
|
# This is tested implicitly as if it doesnt work the rest will die
|
||||||
|
client.set_alarm_state(
|
||||||
|
AlarmName='testalarm1',
|
||||||
|
StateValue='ALARM',
|
||||||
|
StateReason='testreason',
|
||||||
|
StateReasonData='{"some": "json_data"}'
|
||||||
|
)
|
||||||
|
|
||||||
|
resp = client.describe_alarms(
|
||||||
|
StateValue='ALARM'
|
||||||
|
)
|
||||||
|
len(resp['MetricAlarms']).should.equal(1)
|
||||||
|
resp['MetricAlarms'][0]['AlarmName'].should.equal('testalarm1')
|
||||||
|
|
||||||
|
resp = client.describe_alarms(
|
||||||
|
StateValue='OK'
|
||||||
|
)
|
||||||
|
len(resp['MetricAlarms']).should.equal(1)
|
||||||
|
resp['MetricAlarms'][0]['AlarmName'].should.equal('testalarm2')
|
||||||
|
|
||||||
|
# Just for sanity
|
||||||
|
resp = client.describe_alarms()
|
||||||
|
len(resp['MetricAlarms']).should.equal(2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user