Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PR #779/dc379597 backport][stable-2] Create ec2_asg_scheduled_action module #820

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ action_groups:
- ec2_asg
- ec2_asg_facts
- ec2_asg_info
- ec2_asg_scheduled_action
- ec2_asg_lifecycle_hook
- ec2_customer_gateway
- ec2_customer_gateway_facts
Expand Down
320 changes: 320 additions & 0 deletions plugins/modules/ec2_asg_scheduled_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
#!/usr/bin/python

# Copyright: (c) 2021, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

# Based off of https://github.com/mmochan/ansible-aws-ec2-asg-scheduled-actions/blob/master/library/ec2_asg_scheduled_action.py
# (c) 2016, Mike Mochan <@mmochan>

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

DOCUMENTATION = r'''
---
module: ec2_asg_scheduled_action
version_added: 2.2.0
short_description: Create, modify and delete ASG scheduled scaling actions.
description:
- The module will create a new scheduled action when I(state=present) and no given action is found.
- The module will update a new scheduled action when I(state=present) and the given action is found.
- The module will delete a new scheduled action when I(state=absent) and the given action is found.
options:
autoscaling_group_name:
description:
- The name of the autoscaling group to add a scheduled action to.
type: str
required: true
scheduled_action_name:
description:
- The name of the scheduled action.
type: str
required: true
start_time:
description:
- Start time for the action.
type: str
end_time:
description:
- End time for the action.
type: str
time_zone:
description:
- Time zone to run against.
type: str
recurrence:
description:
- Cron style schedule to repeat the action on.
- Required when I(state=present).
type: str
min_size:
description:
- ASG min capacity.
type: int
max_size:
description:
- ASG max capacity.
type: int
desired_capacity:
description:
- ASG desired capacity.
type: int
state:
description:
- Create / update or delete scheduled action.
type: str
required: false
default: present
choices: ['present', 'absent']
author: Mark Woolley(@marknet15)
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
'''

EXAMPLES = r'''
# Create a scheduled action for a autoscaling group.
- name: Create a minimal scheduled action for autoscaling group
community.aws.ec2_asg_scheduled_action:
region: eu-west-1
autoscaling_group_name: test_asg
scheduled_action_name: test_scheduled_action
start_time: 2021 October 25 08:00 UTC
recurrence: 40 22 * * 1-5
desired_capacity: 10
state: present
register: scheduled_action

- name: Create a scheduled action for autoscaling group
community.aws.ec2_asg_scheduled_action:
region: eu-west-1
autoscaling_group_name: test_asg
scheduled_action_name: test_scheduled_action
start_time: 2021 October 25 08:00 UTC
end_time: 2021 October 25 08:00 UTC
time_zone: Europe/London
recurrence: 40 22 * * 1-5
min_size: 10
max_size: 15
desired_capacity: 10
state: present
register: scheduled_action

- name: Delete scheduled action
community.aws.ec2_asg_scheduled_action:
region: eu-west-1
autoscaling_group_name: test_asg
scheduled_action_name: test_scheduled_action
state: absent
'''
RETURN = r'''
scheduled_action_name:
description: The name of the scheduled action.
returned: when I(state=present)
type: str
sample: test_scheduled_action
start_time:
description: Start time for the action.
returned: when I(state=present)
type: str
sample: '2021 October 25 08:00 UTC'
end_time:
description: End time for the action.
returned: when I(state=present)
type: str
sample: '2021 October 25 08:00 UTC'
time_zone:
description: The ID of the Amazon Machine Image used by the launch configuration.
returned: when I(state=present)
type: str
sample: Europe/London
recurrence:
description: Cron style schedule to repeat the action on.
returned: when I(state=present)
type: str
sample: '40 22 * * 1-5'
min_size:
description: ASG min capacity.
returned: when I(state=present)
type: int
sample: 1
max_size:
description: ASG max capacity.
returned: when I(state=present)
type: int
sample: 2
desired_capacity:
description: ASG desired capacity.
returned: when I(state=present)
type: int
sample: 1
'''

try:
import botocore
except ImportError:
pass # caught by AnsibleAWSModule

try:
from dateutil.parser import parse as timedate_parse
HAS_DATEUTIL = True
except ImportError:
HAS_DATEUTIL = False

from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry


def format_request():
params = dict(
AutoScalingGroupName=module.params.get('autoscaling_group_name'),
ScheduledActionName=module.params.get('scheduled_action_name'),
Recurrence=module.params.get('recurrence')
)

# Some of these params are optional
if module.params.get('desired_capacity') is not None:
params['DesiredCapacity'] = module.params.get('desired_capacity')

if module.params.get('min_size') is not None:
params['MinSize'] = module.params.get('min_size')

if module.params.get('max_size') is not None:
params['MaxSize'] = module.params.get('max_size')

if module.params.get('time_zone') is not None:
params['TimeZone'] = module.params.get('time_zone')

if module.params.get('start_time') is not None:
params['StartTime'] = module.params.get('start_time')

if module.params.get('end_time') is not None:
params['EndTime'] = module.params.get('end_time')

return params


def delete_scheduled_action(current_actions):
if current_actions == []:
return False

if module.check_mode:
return True

params = dict(
AutoScalingGroupName=module.params.get('autoscaling_group_name'),
ScheduledActionName=module.params.get('scheduled_action_name')
)

try:
client.delete_scheduled_action(aws_retry=True, **params)
except botocore.exceptions.ClientError as e:
module.fail_json(msg=str(e))

return True


def get_scheduled_actions():
params = dict(
AutoScalingGroupName=module.params.get('autoscaling_group_name'),
ScheduledActionNames=[module.params.get('scheduled_action_name')]
)

try:
actions = client.describe_scheduled_actions(aws_retry=True, **params)
except botocore.exceptions.ClientError as e:
module.fail_json_aws(e)

current_actions = actions.get("ScheduledUpdateGroupActions")

return current_actions


def put_scheduled_update_group_action(current_actions):
changed = False
changes = dict()
params = format_request()

if len(current_actions) < 1:
changed = True
else:
# To correctly detect changes convert the start_time & end_time to datetime object
if "StartTime" in params:
params["StartTime"] = timedate_parse(params["StartTime"])
if "EndTime" in params:
params["EndTime"] = timedate_parse(params["EndTime"])

for k, v in params.items():
if current_actions[0].get(k) != v:
changes[k] = v

if changes:
changed = True

if module.check_mode:
return changed

try:
client.put_scheduled_update_group_action(aws_retry=True, **params)
except botocore.exceptions.ClientError as e:
module.fail_json_aws(e)

return changed


def main():
global module
global client

argument_spec = dict(
autoscaling_group_name=dict(required=True, type='str'),
scheduled_action_name=dict(required=True, type='str'),
start_time=dict(default=None, type='str'),
end_time=dict(default=None, type='str'),
time_zone=dict(default=None, type='str'),
recurrence=dict(type='str'),
min_size=dict(default=None, type='int'),
max_size=dict(default=None, type='int'),
desired_capacity=dict(default=None, type='int'),
state=dict(default='present', choices=['present', 'absent'])
)

module = AnsibleAWSModule(
argument_spec=argument_spec,
required_if=[['state', 'present', ['recurrence']]],
supports_check_mode=True
)

if not HAS_DATEUTIL:
module.fail_json(msg='dateutil is required for this module')

if not module.botocore_at_least("1.20.24"):
module.fail_json(msg='botocore version >= 1.20.24 is required for this module')

client = module.client('autoscaling', retry_decorator=AWSRetry.jittered_backoff())
current_actions = get_scheduled_actions()
state = module.params.get('state')
results = dict()

if state == 'present':
changed = put_scheduled_update_group_action(current_actions)
if not module.check_mode:
updated_action = get_scheduled_actions()[0]
results = dict(
scheduled_action_name=updated_action.get('ScheduledActionName'),
start_time=updated_action.get('StartTime'),
end_time=updated_action.get('EndTime'),
time_zone=updated_action.get('TimeZone'),
recurrence=updated_action.get('Recurrence'),
min_size=updated_action.get('MinSize'),
max_size=updated_action.get('MaxSize'),
desired_capacity=updated_action.get('DesiredCapacity')
)
else:
changed = delete_scheduled_action(current_actions)

results['changed'] = changed
module.exit_json(**results)


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ botocore
boto3
boto

python-dateutil # Used by ec2_asg_scheduled_action

coverage==4.5.4
placebo
mock
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ virtualenv
awscli
# Used for comparing SSH Public keys to the Amazon fingerprints
pycrypto
# Used by ec2_asg_scheduled_action
python-dateutil
1 change: 1 addition & 0 deletions tests/integration/targets/ec2_asg_scheduled_action/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cloud/aws
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
# Amazon Linux 2 AMI 2.0.20211005.0 x86_64 HVM gp2
ec2_ami_name: "amzn2-ami-hvm-2.0.20211005.0-x86_64-gp2"
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies:
- role: setup_botocore_pip
vars:
botocore_version: "1.20.24"
Loading