summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/ansible/modules/cloud/amazon/ec2_metric_alarm.py373
-rw-r--r--test/integration/targets/ec2_metric_alarm/aliases2
-rw-r--r--test/integration/targets/ec2_metric_alarm/defaults/main.yml6
-rw-r--r--test/integration/targets/ec2_metric_alarm/meta/main.yml3
-rw-r--r--test/integration/targets/ec2_metric_alarm/tasks/env_cleanup.yml94
-rw-r--r--test/integration/targets/ec2_metric_alarm/tasks/env_setup.yml62
-rw-r--r--test/integration/targets/ec2_metric_alarm/tasks/main.yml228
-rw-r--r--test/integration/targets/ec2_metric_alarm/vars/main.yml1
8 files changed, 605 insertions, 164 deletions
diff --git a/lib/ansible/modules/cloud/amazon/ec2_metric_alarm.py b/lib/ansible/modules/cloud/amazon/ec2_metric_alarm.py
index 42f1416c81..b221ae49e3 100644
--- a/lib/ansible/modules/cloud/amazon/ec2_metric_alarm.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_metric_alarm.py
@@ -1,6 +1,18 @@
#!/usr/bin/python
-# Copyright: Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# This file is part of Ansible
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import absolute_import, division, print_function
__metaclass__ = type
@@ -51,10 +63,19 @@ options:
type: str
comparison:
description:
- - Determines how the threshold value is compared.
+ - Determines how the threshold value is compared
+ - Symbolic comparison operators have been deprecated, and will be removed in 2.14
required: false
- choices: ['<=','<','>','>=']
type: str
+ choices:
+ - 'GreaterThanOrEqualToThreshold'
+ - 'GreaterThanThreshold'
+ - 'LessThanThreshold'
+ - 'LessThanOrEqualToThreshold'
+ - '<='
+ - '<'
+ - '>='
+ - '>'
threshold:
description:
- Sets the min/max bound for triggering the alarm.
@@ -76,33 +97,33 @@ options:
required: false
type: str
choices:
- - 'Seconds'
- - 'Microseconds'
- - 'Milliseconds'
- - 'Bytes'
- - 'Kilobytes'
- - 'Megabytes'
- - 'Gigabytes'
- - 'Terabytes'
- - 'Bits'
- - 'Kilobits'
- - 'Megabits'
- - 'Gigabits'
- - 'Terabits'
- - 'Percent'
- - 'Count'
- - 'Bytes/Second'
- - 'Kilobytes/Second'
- - 'Megabytes/Second'
- - 'Gigabytes/Second'
- - 'Terabytes/Second'
- - 'Bits/Second'
- - 'Kilobits/Second'
- - 'Megabits/Second'
- - 'Gigabits/Second'
- - 'Terabits/Second'
- - 'Count/Second'
- - 'None'
+ - 'Seconds'
+ - 'Microseconds'
+ - 'Milliseconds'
+ - 'Bytes'
+ - 'Kilobytes'
+ - 'Megabytes'
+ - 'Gigabytes'
+ - 'Terabytes'
+ - 'Bits'
+ - 'Kilobits'
+ - 'Megabits'
+ - 'Gigabits'
+ - 'Terabits'
+ - 'Percent'
+ - 'Count'
+ - 'Bytes/Second'
+ - 'Kilobytes/Second'
+ - 'Megabytes/Second'
+ - 'Gigabytes/Second'
+ - 'Terabytes/Second'
+ - 'Bits/Second'
+ - 'Kilobits/Second'
+ - 'Megabits/Second'
+ - 'Gigabits/Second'
+ - 'Terabits/Second'
+ - 'Count/Second'
+ - 'None'
description:
description:
- A longer description of the alarm.
@@ -133,6 +154,18 @@ options:
required: false
type: list
elements: str
+ treat_missing_data:
+ description:
+ - Sets how the alarm handles missing data points.
+ required: false
+ type: str
+ choices:
+ - 'breaching'
+ - 'notBreaching'
+ - 'ignore'
+ - 'missing'
+ default: 'missing'
+ version_added: "2.10"
extends_documentation_fragment:
- aws
- ec2
@@ -147,7 +180,7 @@ EXAMPLES = '''
metric: "CPUUtilization"
namespace: "AWS/EC2"
statistic: Average
- comparison: "<="
+ comparison: "LessThanOrEqualToThreshold"
threshold: 5.0
period: 300
evaluation_periods: 3
@@ -175,17 +208,12 @@ EXAMPLES = '''
'''
+from ansible.module_utils.aws.core import AnsibleAWSModule
+
try:
- import boto.ec2.cloudwatch
- from boto.ec2.cloudwatch import MetricAlarm
- from boto.exception import BotoServerError, NoAuthHandlerFound
+ from botocore.exceptions import ClientError
except ImportError:
- pass # Taken care of by ec2.HAS_BOTO
-
-import traceback
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ec2 import (AnsibleAWSError, HAS_BOTO, connect_to_aws, ec2_argument_spec,
- get_aws_connection_info)
+ pass # protected by AnsibleAWSModule
def create_metric_alarm(connection, module):
@@ -204,156 +232,173 @@ def create_metric_alarm(connection, module):
alarm_actions = module.params.get('alarm_actions')
insufficient_data_actions = module.params.get('insufficient_data_actions')
ok_actions = module.params.get('ok_actions')
+ treat_missing_data = module.params.get('treat_missing_data')
+
+ warnings = []
+
+ alarms = connection.describe_alarms(AlarmNames=[name])
+
+ comparisons = {'<=': 'LessThanOrEqualToThreshold',
+ '<': 'LessThanThreshold',
+ '>=': 'GreaterThanOrEqualToThreshold',
+ '>': 'GreaterThanThreshold'}
+ if comparison in ('<=', '<', '>', '>='):
+ module.deprecate('Using the <=, <, > and >= operators for comparison has been deprecated. Please use LessThanOrEqualToThreshold, '
+ 'LessThanThreshold, GreaterThanThreshold or GreaterThanOrEqualToThreshold instead.', version="2.14")
+ comparison = comparisons[comparison]
- alarms = None
- try:
- alarms = connection.describe_alarms(alarm_names=[name])
- except BotoServerError as e:
- module.fail_json(msg="Failed to describe alarm %s: %s" % (name, str(e)), exception=traceback.format_exc())
-
- if not alarms:
-
- alm = MetricAlarm(
- name=name,
- metric=metric,
- namespace=namespace,
- statistic=statistic,
- comparison=comparison,
- threshold=threshold,
- period=period,
- evaluation_periods=evaluation_periods,
- unit=unit,
- description=description,
- dimensions=dimensions,
- alarm_actions=alarm_actions,
- insufficient_data_actions=insufficient_data_actions,
- ok_actions=ok_actions
- )
+ if not isinstance(dimensions, list):
+ fixed_dimensions = []
+ for key, value in dimensions.items():
+ fixed_dimensions.append({'Name': key, 'Value': value})
+ dimensions = fixed_dimensions
+
+ if not alarms['MetricAlarms']:
try:
- connection.create_alarm(alm)
+ connection.put_metric_alarm(AlarmName=name,
+ MetricName=metric,
+ Namespace=namespace,
+ Statistic=statistic,
+ ComparisonOperator=comparison,
+ Threshold=threshold,
+ Period=period,
+ EvaluationPeriods=evaluation_periods,
+ Unit=unit,
+ AlarmDescription=description,
+ Dimensions=dimensions,
+ AlarmActions=alarm_actions,
+ InsufficientDataActions=insufficient_data_actions,
+ OKActions=ok_actions,
+ TreatMissingData=treat_missing_data)
changed = True
- alarms = connection.describe_alarms(alarm_names=[name])
- except BotoServerError as e:
- module.fail_json(msg="Failed to create alarm %s: %s" % (name, str(e)), exception=traceback.format_exc())
+ alarms = connection.describe_alarms(AlarmNames=[name])
+ except ClientError as e:
+ module.fail_json_aws(e)
else:
- alarm = alarms[0]
changed = False
-
- for attr in ('comparison', 'metric', 'namespace', 'statistic', 'threshold', 'period', 'evaluation_periods', 'unit', 'description'):
- if getattr(alarm, attr) != module.params.get(attr):
- changed = True
- setattr(alarm, attr, module.params.get(attr))
- # this is to deal with a current bug where you cannot assign '<=>' to the comparator when modifying an existing alarm
- comparison = alarm.comparison
- comparisons = {'<=': 'LessThanOrEqualToThreshold', '<': 'LessThanThreshold', '>=': 'GreaterThanOrEqualToThreshold', '>': 'GreaterThanThreshold'}
- alarm.comparison = comparisons[comparison]
-
- dim1 = module.params.get('dimensions')
- dim2 = alarm.dimensions
-
- for keys in dim1:
- if not isinstance(dim1[keys], list):
- dim1[keys] = [dim1[keys]]
- if keys not in dim2 or dim1[keys] != dim2[keys]:
- changed = True
- setattr(alarm, 'dimensions', dim1)
-
- for attr in ('alarm_actions', 'insufficient_data_actions', 'ok_actions'):
- action = module.params.get(attr) or []
- # Boto and/or ansible may provide same elements in lists but in different order.
- # Compare on sets since they do not need any order.
- if set(getattr(alarm, attr)) != set(action):
+ alarm = alarms['MetricAlarms'][0]
+
+ # Workaround for alarms created before TreatMissingData was introduced
+ if 'TreatMissingData' not in alarm.keys():
+ alarm['TreatMissingData'] = 'missing'
+
+ for key, value in {'MetricName': metric,
+ 'Namespace': namespace,
+ 'Statistic': statistic,
+ 'ComparisonOperator': comparison,
+ 'Threshold': threshold,
+ 'Period': period,
+ 'EvaluationPeriods': evaluation_periods,
+ 'Unit': unit,
+ 'AlarmDescription': description,
+ 'Dimensions': dimensions,
+ 'TreatMissingData': treat_missing_data}.items():
+ try:
+ if alarm[key] != value:
+ changed = True
+ except KeyError:
+ if value is not None:
+ changed = True
+
+ alarm[key] = value
+
+ for key, value in {'AlarmActions': alarm_actions,
+ 'InsufficientDataActions': insufficient_data_actions,
+ 'OKActions': ok_actions}.items():
+ action = value or []
+ if alarm[key] != action:
changed = True
- setattr(alarm, attr, module.params.get(attr))
+ alarm[key] = value
try:
if changed:
- connection.create_alarm(alarm)
- except BotoServerError as e:
- module.fail_json(msg=str(e))
- result = alarms[0]
- module.exit_json(changed=changed, name=result.name,
- actions_enabled=result.actions_enabled,
- alarm_actions=result.alarm_actions,
- alarm_arn=result.alarm_arn,
- comparison=result.comparison,
- description=result.description,
- dimensions=result.dimensions,
- evaluation_periods=result.evaluation_periods,
- insufficient_data_actions=result.insufficient_data_actions,
- last_updated=result.last_updated,
- metric=result.metric,
- namespace=result.namespace,
- ok_actions=result.ok_actions,
- period=result.period,
- state_reason=result.state_reason,
- state_value=result.state_value,
- statistic=result.statistic,
- threshold=result.threshold,
- unit=result.unit)
+ connection.put_metric_alarm(AlarmName=alarm['AlarmName'],
+ MetricName=alarm['MetricName'],
+ Namespace=alarm['Namespace'],
+ Statistic=alarm['Statistic'],
+ ComparisonOperator=alarm['ComparisonOperator'],
+ Threshold=alarm['Threshold'],
+ Period=alarm['Period'],
+ EvaluationPeriods=alarm['EvaluationPeriods'],
+ Unit=alarm['Unit'],
+ AlarmDescription=alarm['AlarmDescription'],
+ Dimensions=alarm['Dimensions'],
+ AlarmActions=alarm['AlarmActions'],
+ InsufficientDataActions=alarm['InsufficientDataActions'],
+ OKActions=alarm['OKActions'],
+ TreatMissingData=alarm['TreatMissingData'])
+ except ClientError as e:
+ module.fail_json_aws(e)
+
+ result = alarms['MetricAlarms'][0]
+ module.exit_json(changed=changed, warnings=warnings,
+ name=result['AlarmName'],
+ actions_enabled=result['ActionsEnabled'],
+ alarm_actions=result['AlarmActions'],
+ alarm_arn=result['AlarmArn'],
+ comparison=result['ComparisonOperator'],
+ description=result['AlarmDescription'],
+ dimensions=result['Dimensions'],
+ evaluation_periods=result['EvaluationPeriods'],
+ insufficient_data_actions=result['InsufficientDataActions'],
+ last_updated=result['AlarmConfigurationUpdatedTimestamp'],
+ metric=result['MetricName'],
+ namespace=result['Namespace'],
+ ok_actions=result['OKActions'],
+ period=result['Period'],
+ state_reason=result['StateReason'],
+ state_value=result['StateValue'],
+ statistic=result['Statistic'],
+ threshold=result['Threshold'],
+ treat_missing_data=result['TreatMissingData'],
+ unit=result['Unit'])
def delete_metric_alarm(connection, module):
name = module.params.get('name')
+ alarms = connection.describe_alarms(AlarmNames=[name])
- alarms = None
- try:
- alarms = connection.describe_alarms(alarm_names=[name])
- except BotoServerError as e:
- module.fail_json(msg="Failed to describe alarm %s: %s" % (name, str(e)), exception=traceback.format_exc())
-
- if alarms:
+ if alarms['MetricAlarms']:
try:
- connection.delete_alarms([name])
+ connection.delete_alarms(AlarmNames=[name])
module.exit_json(changed=True)
- except BotoServerError as e:
- module.fail_json(msg=str(e))
+ except (ClientError) as e:
+ module.fail_json_aws(e)
else:
module.exit_json(changed=False)
def main():
- argument_spec = ec2_argument_spec()
- argument_spec.update(
- dict(
- name=dict(required=True, type='str'),
- metric=dict(type='str'),
- namespace=dict(type='str'),
- statistic=dict(type='str', choices=['SampleCount', 'Average', 'Sum', 'Minimum', 'Maximum']),
- comparison=dict(type='str', choices=['<=', '<', '>', '>=']),
- threshold=dict(type='float'),
- period=dict(type='int'),
- unit=dict(type='str', choices=['Seconds', 'Microseconds', 'Milliseconds', 'Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', 'Terabytes',
- 'Bits', 'Kilobits', 'Megabits', 'Gigabits', 'Terabits', 'Percent', 'Count', 'Bytes/Second', 'Kilobytes/Second',
- 'Megabytes/Second', 'Gigabytes/Second', 'Terabytes/Second', 'Bits/Second', 'Kilobits/Second', 'Megabits/Second',
- 'Gigabits/Second', 'Terabits/Second', 'Count/Second', 'None']),
- evaluation_periods=dict(type='int'),
- description=dict(type='str'),
- dimensions=dict(type='dict', default={}),
- alarm_actions=dict(type='list'),
- insufficient_data_actions=dict(type='list'),
- ok_actions=dict(type='list'),
- state=dict(default='present', choices=['present', 'absent']),
- )
+ argument_spec = dict(
+ name=dict(required=True, type='str'),
+ metric=dict(type='str'),
+ namespace=dict(type='str'),
+ statistic=dict(type='str', choices=['SampleCount', 'Average', 'Sum', 'Minimum', 'Maximum']),
+ comparison=dict(type='str', choices=['LessThanOrEqualToThreshold', 'LessThanThreshold', 'GreaterThanThreshold',
+ 'GreaterThanOrEqualToThreshold', '<=', '<', '>', '>=']),
+ threshold=dict(type='float'),
+ period=dict(type='int'),
+ unit=dict(type='str', choices=['Seconds', 'Microseconds', 'Milliseconds', 'Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes',
+ 'Terabytes', 'Bits', 'Kilobits', 'Megabits', 'Gigabits', 'Terabits', 'Percent', 'Count',
+ 'Bytes/Second', 'Kilobytes/Second', 'Megabytes/Second', 'Gigabytes/Second',
+ 'Terabytes/Second', 'Bits/Second', 'Kilobits/Second', 'Megabits/Second', 'Gigabits/Second',
+ 'Terabits/Second', 'Count/Second', 'None']),
+ evaluation_periods=dict(type='int'),
+ description=dict(type='str'),
+ dimensions=dict(type='dict', default={}),
+ alarm_actions=dict(type='list', default=[]),
+ insufficient_data_actions=dict(type='list', default=[]),
+ ok_actions=dict(type='list', default=[]),
+ treat_missing_data=dict(type='str', choices=['breaching', 'notBreaching', 'ignore', 'missing'], default='missing'),
+ state=dict(default='present', choices=['present', 'absent']),
)
- module = AnsibleModule(argument_spec=argument_spec)
-
- if not HAS_BOTO:
- module.fail_json(msg='boto required for this module')
+ module = AnsibleAWSModule(argument_spec=argument_spec)
state = module.params.get('state')
- region, ec2_url, aws_connect_params = get_aws_connection_info(module)
-
- if region:
- try:
- connection = connect_to_aws(boto.ec2.cloudwatch, region, **aws_connect_params)
- except (NoAuthHandlerFound, AnsibleAWSError) as e:
- module.fail_json(msg=str(e))
- else:
- module.fail_json(msg="region must be specified")
+ connection = module.client('cloudwatch')
if state == 'present':
create_metric_alarm(connection, module)
diff --git a/test/integration/targets/ec2_metric_alarm/aliases b/test/integration/targets/ec2_metric_alarm/aliases
new file mode 100644
index 0000000000..a112c3d1bb
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/aliases
@@ -0,0 +1,2 @@
+cloud/aws
+shippable/aws/group1
diff --git a/test/integration/targets/ec2_metric_alarm/defaults/main.yml b/test/integration/targets/ec2_metric_alarm/defaults/main.yml
new file mode 100644
index 0000000000..4d80b5d6e0
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+# defaults file for ec2_instance
+ec2_instance_name: '{{ resource_prefix }}-node'
+ec2_instance_owner: 'integration-run-{{ resource_prefix }}'
+ec2_ami_name: "amzn-ami-hvm*"
+alarm_prefix: "ansible-test"
diff --git a/test/integration/targets/ec2_metric_alarm/meta/main.yml b/test/integration/targets/ec2_metric_alarm/meta/main.yml
new file mode 100644
index 0000000000..1f64f1169a
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/meta/main.yml
@@ -0,0 +1,3 @@
+dependencies:
+ - prepare_tests
+ - setup_ec2
diff --git a/test/integration/targets/ec2_metric_alarm/tasks/env_cleanup.yml b/test/integration/targets/ec2_metric_alarm/tasks/env_cleanup.yml
new file mode 100644
index 0000000000..e90ddc6450
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/tasks/env_cleanup.yml
@@ -0,0 +1,94 @@
+- name: remove any instances in the test VPC
+ ec2_instance:
+ filters:
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ state: absent
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
+
+- name: remove ENIs
+ ec2_eni_info:
+ filters:
+ vpc-id: "{{ testing_vpc.vpc.id }}"
+ register: enis
+
+- name: delete all ENIs
+ ec2_eni:
+ eni_id: "{{ item.id }}"
+ state: absent
+ until: removed is not failed
+ with_items: "{{ enis.network_interfaces }}"
+ ignore_errors: yes
+ retries: 10
+
+- name: remove the security group
+ ec2_group:
+ name: "{{ resource_prefix }}-sg"
+ description: a security group for ansible tests
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ state: absent
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
+
+- name: remove routing rules
+ ec2_vpc_route_table:
+ state: absent
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ tags:
+ created: "{{ resource_prefix }}-route"
+ routes:
+ - dest: 0.0.0.0/0
+ gateway_id: "{{ igw.gateway_id }}"
+ subnets:
+ - "{{ testing_subnet_a.subnet.id }}"
+ - "{{ testing_subnet_b.subnet.id }}"
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
+
+- name: remove internet gateway
+ ec2_vpc_igw:
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ state: absent
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
+
+- name: remove subnet A
+ ec2_vpc_subnet:
+ state: absent
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ cidr: 10.22.32.0/24
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
+
+- name: remove subnet B
+ ec2_vpc_subnet:
+ state: absent
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ cidr: 10.22.33.0/24
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
+
+- name: remove the VPC
+ ec2_vpc_net:
+ name: "{{ resource_prefix }}-vpc"
+ cidr_block: 10.22.32.0/23
+ state: absent
+ tags:
+ Name: Ansible Testing VPC
+ tenancy: default
+ register: removed
+ until: removed is not failed
+ ignore_errors: yes
+ retries: 10
diff --git a/test/integration/targets/ec2_metric_alarm/tasks/env_setup.yml b/test/integration/targets/ec2_metric_alarm/tasks/env_setup.yml
new file mode 100644
index 0000000000..80b49dbcf7
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/tasks/env_setup.yml
@@ -0,0 +1,62 @@
+- name: Create VPC for use in testing
+ ec2_vpc_net:
+ name: "{{ resource_prefix }}-vpc"
+ cidr_block: 10.22.32.0/23
+ tags:
+ Name: Ansible ec2_instance Testing VPC
+ tenancy: default
+ register: testing_vpc
+
+- name: Create internet gateway for use in testing
+ ec2_vpc_igw:
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ state: present
+ register: igw
+
+- name: Create default subnet in zone A
+ ec2_vpc_subnet:
+ state: present
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ cidr: 10.22.32.0/24
+ az: "{{ aws_region }}a"
+ resource_tags:
+ Name: "{{ resource_prefix }}-subnet-a"
+ register: testing_subnet_a
+
+- name: Create secondary subnet in zone B
+ ec2_vpc_subnet:
+ state: present
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ cidr: 10.22.33.0/24
+ az: "{{ aws_region }}b"
+ resource_tags:
+ Name: "{{ resource_prefix }}-subnet-b"
+ register: testing_subnet_b
+
+- name: create routing rules
+ ec2_vpc_route_table:
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ tags:
+ created: "{{ resource_prefix }}-route"
+ routes:
+ - dest: 0.0.0.0/0
+ gateway_id: "{{ igw.gateway_id }}"
+ subnets:
+ - "{{ testing_subnet_a.subnet.id }}"
+ - "{{ testing_subnet_b.subnet.id }}"
+
+- name: create a security group with the vpc
+ ec2_group:
+ name: "{{ resource_prefix }}-sg"
+ description: a security group for ansible tests
+ vpc_id: "{{ testing_vpc.vpc.id }}"
+ rules:
+ - proto: tcp
+ from_port: 22
+ to_port: 22
+ cidr_ip: 0.0.0.0/0
+ - proto: tcp
+ from_port: 80
+ to_port: 80
+ cidr_ip: 0.0.0.0/0
+ register: sg
diff --git a/test/integration/targets/ec2_metric_alarm/tasks/main.yml b/test/integration/targets/ec2_metric_alarm/tasks/main.yml
new file mode 100644
index 0000000000..f3f645cb2a
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/tasks/main.yml
@@ -0,0 +1,228 @@
+- name: run ec2_metric_alarm tests
+ module_defaults:
+ group/aws:
+ aws_access_key: "{{ aws_access_key }}"
+ aws_secret_key: "{{ aws_secret_key }}"
+ security_token: "{{ security_token | default(omit) }}"
+ region: "{{ aws_region }}"
+ block:
+ - set_fact:
+ alarm_full_name: "{{ alarm_prefix }}-{{ resource_prefix }}-cpu-low"
+
+ # until there's a module to get info about alarms, awscli is needed
+ - name: install awscli
+ pip:
+ state: present
+ name: awscli
+
+ - name: set up environment for testing.
+ include_tasks: env_setup.yml
+
+ - name: get info on alarms
+ command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }}
+ environment:
+ AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
+ AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
+ AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
+ AWS_DEFAULT_REGION: "{{ aws_region }}"
+ register: alarm_info_query
+
+ - name: Find AMI to use
+ ec2_ami_info:
+ owners: 'amazon'
+ filters:
+ name: '{{ ec2_ami_name }}'
+ register: ec2_amis
+ - set_fact:
+ ec2_ami_image: '{{ ec2_amis.images[0].image_id }}'
+
+ - name: Make instance in a default subnet of the VPC
+ ec2_instance:
+ name: "{{ resource_prefix }}-test-default-vpc"
+ image_id: "{{ec2_ami_image }}"
+ tags:
+ TestId: "{{ resource_prefix }}"
+ security_groups: "{{ sg.group_id }}"
+ instance_type: t2.micro
+ wait: true
+ register: ec2_instance_results
+
+ - name: create ec2 metric alarm on ec2 instance
+ ec2_metric_alarm:
+ dimensions:
+ InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}"
+ state: present
+ name: "{{ alarm_full_name }}"
+ metric: "CPUUtilization"
+ namespace: "AWS/EC2"
+ treat_missing_data: missing
+ statistic: Average
+ comparison: "<="
+ threshold: 5.0
+ period: 300
+ evaluation_periods: 3
+ unit: "Percent"
+ description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes "
+ register: ec2_instance_metric_alarm
+
+ - name: get info on alarms
+ command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }}
+ environment:
+ AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
+ AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
+ AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
+ AWS_DEFAULT_REGION: "{{ aws_region }}"
+ register: alarm_info_query
+
+ - name: convert it to an object
+ set_fact:
+ alarm_info: "{{ alarm_info_query.stdout |from_json }}"
+
+ - name: "verify that an alarm was created"
+ assert:
+ that:
+ - 'ec2_instance_metric_alarm.changed'
+ - 'ec2_instance_metric_alarm.alarm_arn'
+ - 'ec2_instance_metric_alarm.statistic == alarm_info["MetricAlarms"][0].Statistic'
+ - 'ec2_instance_metric_alarm.name == alarm_info["MetricAlarms"][0].AlarmName'
+ - 'ec2_instance_metric_alarm.metric== alarm_info["MetricAlarms"][0].MetricName'
+ - 'ec2_instance_metric_alarm.namespace == alarm_info["MetricAlarms"][0].Namespace'
+ - 'ec2_instance_metric_alarm.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator'
+ - 'ec2_instance_metric_alarm.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator'
+ - 'ec2_instance_metric_alarm.threshold == alarm_info["MetricAlarms"][0].Threshold'
+ - 'ec2_instance_metric_alarm.period == alarm_info["MetricAlarms"][0].Period'
+ - 'ec2_instance_metric_alarm.unit == alarm_info["MetricAlarms"][0].Unit'
+ - 'ec2_instance_metric_alarm.evaluation_periods == alarm_info["MetricAlarms"][0].EvaluationPeriods'
+ - 'ec2_instance_metric_alarm.description == alarm_info["MetricAlarms"][0].AlarmDescription'
+ - 'ec2_instance_metric_alarm.treat_missing_data == alarm_info["MetricAlarms"][0].TreatMissingData'
+
+ - name: create ec2 metric alarm on ec2 instance (idempotent)
+ ec2_metric_alarm:
+ dimensions:
+ InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}"
+ state: present
+ name: "{{ alarm_full_name }}"
+ metric: "CPUUtilization"
+ namespace: "AWS/EC2"
+ treat_missing_data: missing
+ statistic: Average
+ comparison: "<="
+ threshold: 5.0
+ period: 300
+ evaluation_periods: 3
+ unit: "Percent"
+ description: "This will alarm when an instance's cpu usage average is lower than 5% for 15 minutes "
+ register: ec2_instance_metric_alarm_idempotent
+
+ - name: get info on alarms
+ command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }}
+ environment:
+ AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
+ AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
+ AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
+ AWS_DEFAULT_REGION: "{{ aws_region }}"
+ register: alarm_info_query_idempotent
+
+ - name: convert it to an object
+ set_fact:
+ alarm_info_idempotent: "{{ alarm_info_query_idempotent.stdout |from_json }}"
+
+ - name: "Verify alarm does not register as changed after update"
+ assert:
+ that:
+ - not ec2_instance_metric_alarm_idempotent.changed
+
+ - name: "Verify alarm did not change after updating"
+ assert:
+ that:
+ - "alarm_info['MetricAlarms'][0]['{{item}}'] == alarm_info_idempotent['MetricAlarms'][0]['{{ item }}']"
+ with_items:
+ - AlarmArn
+ - Statistic
+ - AlarmName
+ - MetricName
+ - Namespace
+ - ComparisonOperator
+ - Threshold
+ - Period
+ - Unit
+ - EvaluationPeriods
+ - AlarmDescription
+ - TreatMissingData
+
+ - name: update alarm
+ ec2_metric_alarm:
+ dimensions:
+ InstanceId: "{{ ec2_instance_results.instances[0].instance_id }}"
+ state: present
+ name: "{{ alarm_full_name }}"
+ metric: "CPUUtilization"
+ namespace: "AWS/EC2"
+ statistic: Average
+ comparison: "<="
+ threshold: 5.0
+ period: 60
+ evaluation_periods: 3
+ unit: "Percent"
+ description: "This will alarm when an instance's cpu usage average is lower than 5% for 3 minutes "
+ register: ec2_instance_metric_alarm_update
+
+ - name: "verify that alarm registers as updated"
+ assert:
+ that:
+ - 'ec2_instance_metric_alarm.changed'
+
+ - name: "verify that properties were changed"
+ assert:
+ that:
+ - 'ec2_instance_metric_alarm_update.changed'
+ - 'ec2_instance_metric_alarm_update.period == 60' #Period should be 60, not matching old value
+ - 'ec2_instance_metric_alarm_update.alarm_arn == ec2_instance_metric_alarm.alarm_arn'
+ - 'ec2_instance_metric_alarm_update.statistic == alarm_info["MetricAlarms"][0].Statistic'
+ - 'ec2_instance_metric_alarm_update.name == alarm_info["MetricAlarms"][0].AlarmName'
+ - 'ec2_instance_metric_alarm_update.metric== alarm_info["MetricAlarms"][0].MetricName'
+ - 'ec2_instance_metric_alarm_update.namespace == alarm_info["MetricAlarms"][0].Namespace'
+ - 'ec2_instance_metric_alarm_update.statistic == alarm_info["MetricAlarms"][0].Statistic'
+ - 'ec2_instance_metric_alarm_update.comparison == alarm_info["MetricAlarms"][0].ComparisonOperator'
+ - 'ec2_instance_metric_alarm_update.threshold == alarm_info["MetricAlarms"][0].Threshold'
+ - 'ec2_instance_metric_alarm_update.unit == alarm_info["MetricAlarms"][0].Unit'
+ - 'ec2_instance_metric_alarm_update.evaluation_periods == alarm_info["MetricAlarms"][0].EvaluationPeriods'
+ - 'ec2_instance_metric_alarm_update.treat_missing_data == alarm_info["MetricAlarms"][0].TreatMissingData'
+
+ - name: try to remove the alarm
+ ec2_metric_alarm:
+ state: absent
+ name: "{{ alarm_full_name }}"
+
+ register: ec2_instance_metric_alarm_deletion
+
+ - name: Verify that the alarm reports deleted/changed
+ assert:
+ that:
+ - 'ec2_instance_metric_alarm_deletion.changed'
+
+ - name: get info on alarms
+ command: aws cloudwatch describe-alarms --alarm-names {{ alarm_full_name }}
+ environment:
+ AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
+ AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
+ AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
+ AWS_DEFAULT_REGION: "{{ aws_region }}"
+ register: alarm_info_query
+
+ - name: convert it to an object
+ set_fact:
+ alarm_info: "{{ alarm_info_query.stdout |from_json }}"
+
+ - name: Verify that the alarm was deleted using cli
+ assert:
+ that:
+ - 'alarm_info["MetricAlarms"]|length == 0'
+ always:
+ - name: try to stop the ec2 instance
+ ec2_instance:
+ instance_ids: "{{ ec2_instance_results.instances[0].instance_id }}"
+ state: terminated
+ ignore_errors: yes
+
+ - include_tasks: env_cleanup.yml
diff --git a/test/integration/targets/ec2_metric_alarm/vars/main.yml b/test/integration/targets/ec2_metric_alarm/vars/main.yml
new file mode 100644
index 0000000000..ed97d539c0
--- /dev/null
+++ b/test/integration/targets/ec2_metric_alarm/vars/main.yml
@@ -0,0 +1 @@
+---