From 93f56322d88857b04cbb58bdef52b51b6c88ce5f Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Thu, 30 Oct 2014 23:51:09 -0400 Subject: [PATCH] Fix merge conflicts for cloudwatch. --- moto/__init__.py | 1 + moto/backends.py | 2 + moto/cloudwatch/__init__.py | 2 + moto/cloudwatch/models.py | 50 +++++++++ moto/cloudwatch/responses.py | 129 +++++++++++++++++++++++ moto/cloudwatch/urls.py | 9 ++ tests/test_cloudwatch/test_cloudwatch.py | 71 +++++++++++++ 7 files changed, 264 insertions(+) create mode 100644 moto/cloudwatch/__init__.py create mode 100644 moto/cloudwatch/models.py create mode 100644 moto/cloudwatch/responses.py create mode 100644 moto/cloudwatch/urls.py create mode 100644 tests/test_cloudwatch/test_cloudwatch.py diff --git a/moto/__init__.py b/moto/__init__.py index fa1fa6ec0..df128c259 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -4,6 +4,7 @@ logging.getLogger('boto').setLevel(logging.CRITICAL) from .autoscaling import mock_autoscaling from .cloudformation import mock_cloudformation +from .cloudwatch import mock_cloudwatch from .dynamodb import mock_dynamodb from .dynamodb2 import mock_dynamodb2 from .ec2 import mock_ec2 diff --git a/moto/backends.py b/moto/backends.py index 6ee602ea5..3a50f5769 100644 --- a/moto/backends.py +++ b/moto/backends.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals from moto.autoscaling import autoscaling_backend +from moto.cloudwatch import cloudwatch_backend from moto.dynamodb import dynamodb_backend from moto.dynamodb2 import dynamodb_backend2 from moto.ec2 import ec2_backend @@ -14,6 +15,7 @@ from moto.route53 import route53_backend BACKENDS = { 'autoscaling': autoscaling_backend, + 'cloudwatch': cloudwatch_backend, 'dynamodb': dynamodb_backend, 'dynamodb2': dynamodb_backend2, 'ec2': ec2_backend, diff --git a/moto/cloudwatch/__init__.py b/moto/cloudwatch/__init__.py new file mode 100644 index 000000000..8ea4bf261 --- /dev/null +++ b/moto/cloudwatch/__init__.py @@ -0,0 +1,2 @@ +from .models import cloudwatch_backend +mock_cloudwatch = cloudwatch_backend.decorator diff --git a/moto/cloudwatch/models.py b/moto/cloudwatch/models.py new file mode 100644 index 000000000..43be48b36 --- /dev/null +++ b/moto/cloudwatch/models.py @@ -0,0 +1,50 @@ +from moto.core import BaseBackend + + +class Dimension(object): + def __init__(self, name, value): + self.name = name + self.value = value + + +class FakeAlarm(object): + def __init__(self, name, comparison_operator, evaluation_periods, period, + threshold, statistic, description, dimensions, alarm_actions, + ok_actions, insufficient_data_actions, unit): + self.name = name + self.comparison_operator = comparison_operator + self.evaluation_periods = evaluation_periods + self.period = period + self.threshold = threshold + self.statistic = statistic + self.description = description + self.dimensions = [Dimension(dimension['name'], dimension['value']) for dimension in dimensions] + self.alarm_actions = alarm_actions + self.ok_actions = ok_actions + self.insufficient_data_actions = insufficient_data_actions + self.unit = unit + + +class CloudWatchBackend(BaseBackend): + + def __init__(self): + self.alarms = {} + + def put_metric_alarm(self, name, comparison_operator, evaluation_periods, + period, threshold, statistic, description, dimensions, + alarm_actions, ok_actions, insufficient_data_actions, unit): + alarm = FakeAlarm(name, comparison_operator, evaluation_periods, period, + threshold, statistic, description, dimensions, alarm_actions, + ok_actions, insufficient_data_actions, unit) + self.alarms[name] = alarm + return alarm + + def get_all_alarms(self): + return self.alarms.values() + + def delete_alarms(self, alarm_names): + for alarm_name in alarm_names: + self.alarms.pop(alarm_name, None) + + +cloudwatch_backend = CloudWatchBackend() diff --git a/moto/cloudwatch/responses.py b/moto/cloudwatch/responses.py new file mode 100644 index 000000000..96fecfee6 --- /dev/null +++ b/moto/cloudwatch/responses.py @@ -0,0 +1,129 @@ +from jinja2 import Template + +from moto.core.responses import BaseResponse +from moto.core.utils import camelcase_to_underscores +from .models import cloudwatch_backend + + +class CloudWatchResponse(BaseResponse): + + def _get_param(self, param_name): + return self.querystring.get(param_name, [None])[0] + + def _get_multi_param(self, param_prefix): + return [value[0] for key, value in self.querystring.items() if key.startswith(param_prefix)] + + def _get_list_prefix(self, param_prefix): + results = [] + param_index = 1 + while True: + index_prefix = "{}.{}.".format(param_prefix, param_index) + new_items = { + camelcase_to_underscores(key.replace(index_prefix, "")): value[0] + for key, value in self.querystring.items() + if key.startswith(index_prefix) + } + if not new_items: + break + results.append(new_items) + param_index += 1 + return results + + def put_metric_alarm(self): + name = self._get_param('AlarmName') + comparison_operator = self._get_param('ComparisonOperator') + evaluation_periods = self._get_param('EvaluationPeriods') + period = self._get_param('Period') + threshold = self._get_param('Threshold') + statistic = self._get_param('Statistic') + description = self._get_param('AlarmDescription') + dimensions = self._get_list_prefix('Dimensions.member') + alarm_actions = self._get_multi_param('AlarmActions') + ok_actions = self._get_multi_param('OKActions') + insufficient_data_actions = self._get_multi_param("InsufficientDataActions") + unit = self._get_param('Unit') + alarm = cloudwatch_backend.put_metric_alarm(name, comparison_operator, + evaluation_periods, period, + threshold, statistic, + description, dimensions, + alarm_actions, ok_actions, + insufficient_data_actions, + unit) + template = Template(PUT_METRIC_ALARM_TEMPLATE) + return template.render(alarm=alarm) + + def describe_alarms(self): + alarms = cloudwatch_backend.get_all_alarms() + template = Template(DESCRIBE_ALARMS_TEMPLATE) + return template.render(alarms=alarms) + + def delete_alarms(self): + alarm_names = self._get_multi_param('AlarmNames.member') + cloudwatch_backend.delete_alarms(alarm_names) + template = Template(DELETE_METRIC_ALARMS_TEMPLATE) + return template.render() + +PUT_METRIC_ALARM_TEMPLATE = """ + + + 2690d7eb-ed86-11dd-9877-6fad448a8419 + + +""" + +DESCRIBE_ALARMS_TEMPLATE = """ + + {% for alarm in alarms %} + + {{ alarm.actions_enabled }} + + {% for action in alarm.alarm_actions %} + {{ action }} + {% endfor %} + + {{ alarm.arn }} + {{ alarm.configuration_updated_timestamp }} + {{ alarm.description }} + {{ alarm.name }} + {{ alarm.comparison_operator }} + + {% for dimension in alarm.dimensions %} + + {{ dimension.name }} + {{ dimension.value }} + + {% endfor %} + + {{ alarm.evaluation_periods }} + + {% for action in alarm.insufficient_data_actions %} + {{ action }} + {% endfor %} + + {{ alarm.metric_name }} + {{ alarm.namespace }} + + {% for action in alarm.ok_actions %} + {{ action }} + {% endfor %} + + {{ alarm.period }} + {{ alarm.state_reason }} + {{ alarm.state_reason_data }} + {{ alarm.state_updated_timestamp }} + {{ alarm.state_value }} + {{ alarm.statistic }} + {{ alarm.threshold }} + {{ alarm.unit }} + + {% endfor %} + +""" + +DELETE_METRIC_ALARMS_TEMPLATE = """ + + + 2690d7eb-ed86-11dd-9877-6fad448a8419 + + +""" diff --git a/moto/cloudwatch/urls.py b/moto/cloudwatch/urls.py new file mode 100644 index 000000000..2371f09cb --- /dev/null +++ b/moto/cloudwatch/urls.py @@ -0,0 +1,9 @@ +from .responses import CloudWatchResponse + +url_bases = [ + "https?://monitoring.(.+).amazonaws.com", +] + +url_paths = { + '{0}/$': CloudWatchResponse().dispatch, +} diff --git a/tests/test_cloudwatch/test_cloudwatch.py b/tests/test_cloudwatch/test_cloudwatch.py new file mode 100644 index 000000000..df3e41762 --- /dev/null +++ b/tests/test_cloudwatch/test_cloudwatch.py @@ -0,0 +1,71 @@ +import boto +from boto.ec2.cloudwatch.alarm import MetricAlarm +import sure # noqa + +from moto import mock_cloudwatch + + +@mock_cloudwatch +def test_create_alarm(): + conn = boto.connect_cloudwatch() + + alarm = MetricAlarm( + name='tester', + comparison='>=', + threshold=2.0, + period=60, + evaluation_periods=5, + statistic='Average', + description='A test', + dimensions={'InstanceId': ['i-0123456,i-0123457']}, + alarm_actions=['arn:alarm'], + ok_actions=['arn:ok'], + insufficient_data_actions=['arn:insufficient'], + unit='Seconds', + ) + conn.create_alarm(alarm) + + alarms = conn.describe_alarms() + alarms.should.have.length_of(1) + alarm = alarms[0] + alarm.name.should.equal('tester') + alarm.comparison.should.equal('>=') + alarm.threshold.should.equal(2.0) + alarm.period.should.equal(60) + alarm.evaluation_periods.should.equal(5) + alarm.statistic.should.equal('Average') + alarm.description.should.equal('A test') + dict(alarm.dimensions).should.equal({'InstanceId': ['i-0123456,i-0123457']}) + list(alarm.alarm_actions).should.equal(['arn:alarm']) + list(alarm.ok_actions).should.equal(['arn:ok']) + list(alarm.insufficient_data_actions).should.equal(['arn:insufficient']) + alarm.unit.should.equal('Seconds') + + +@mock_cloudwatch +def test_delete_alarm(): + conn = boto.connect_cloudwatch() + + alarm = MetricAlarm( + name='tester', + comparison='>=', + threshold=2.0, + period=60, + evaluation_periods=5, + statistic='Average', + description='A test', + dimensions={'InstanceId': ['i-0123456,i-0123457']}, + alarm_actions=['arn:alarm'], + ok_actions=['arn:ok'], + insufficient_data_actions=['arn:insufficient'], + unit='Seconds', + ) + conn.create_alarm(alarm) + + alarms = conn.describe_alarms() + alarms.should.have.length_of(1) + + alarms[0].delete() + + alarms = conn.describe_alarms() + alarms.should.have.length_of(0)