More explicit argspec tests (#72064)

* Add more explicit coverage of argspec functionality

* fail_on_missing_params

* ci_complete ci_coverage

* Remove incidental_aws_step_functions_state_machine

* ci_complete ci_coverage

* Remove incidental_cs_service_offering

* ci_complete ci_coverage
pull/25260/head
Matt Martz 4 years ago committed by GitHub
parent a7170da851
commit ab2b339dd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -42,6 +42,14 @@ def main():
'another': {}, 'another': {},
} }
}, },
'json': {
'type': 'json',
},
'fail_on_missing_params': {
'type': 'list',
'default': [],
},
'needed_param': {},
}, },
required_if=( required_if=(
('state', 'present', ('path', 'content'), True), ('state', 'present', ('path', 'content'), True),
@ -51,6 +59,8 @@ def main():
), ),
) )
module.fail_on_missing_params(module.params['fail_on_missing_params'])
module.exit_json(**module.params) module.exit_json(**module.params)

@ -87,6 +87,31 @@
register: argpsec_required_if_fail register: argpsec_required_if_fail
ignore_errors: true ignore_errors: true
- argspec:
json: !!str '{"foo": "bar"}'
register: argspec_good_json_string
- argspec:
json:
foo: bar
register: argspec_good_json_dict
- argspec:
json: 1
register: argspec_bad_json
ignore_errors: true
- argspec:
fail_on_missing_params:
- needed_param
needed_param: whatever
- argspec:
fail_on_missing_params:
- needed_param
register: argspec_fail_on_missing_params_bad
ignore_errors: true
- assert: - assert:
that: that:
- argspec_required_if_fail is failed - argspec_required_if_fail is failed
@ -109,3 +134,13 @@
- argspec_required_together_fail is failed - argspec_required_together_fail is failed
- argpsec_required_if_fail is failed - argpsec_required_if_fail is failed
- argspec_good_json_string is successful
- >
argspec_good_json_string.json == '{"foo": "bar"}'
- argspec_good_json_dict is successful
- >
argspec_good_json_dict.json == '{"foo": "bar"}'
- argspec_bad_json is failed
- argspec_fail_on_missing_params_bad is failed

@ -1,4 +0,0 @@
# the random_num is generated in a set_fact task at the start of the testsuite
state_machine_name: "{{ resource_prefix }}_step_functions_state_machine_ansible_test_{{ random_num }}"
step_functions_role_name: "ansible-test-sts-{{ resource_prefix }}-step_functions-role"
execution_name: "{{ resource_prefix }}_sfn_execution"

@ -1,15 +0,0 @@
{
"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Pass",
"Result": "Some other result",
"Next": "Wait"
},
"Wait": {
"Type": "Wait",
"Seconds": 30,
"End": true
}
}
}

@ -1,10 +0,0 @@
{
"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Pass",
"Result": "Hello World!",
"End": true
}
}
}

@ -1,12 +0,0 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "states.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

@ -1,296 +0,0 @@
---
- name: Integration test for AWS Step Function state machine module
module_defaults:
iam_role:
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region }}"
aws_step_functions_state_machine:
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region }}"
aws_step_functions_state_machine_execution:
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token | default(omit) }}"
region: "{{ aws_region }}"
block:
# ==== Setup ==================================================
- name: Create IAM service role needed for Step Functions
iam_role:
name: "{{ step_functions_role_name }}"
description: Role with permissions for AWS Step Functions actions.
assume_role_policy_document: "{{ lookup('file', 'state_machines_iam_trust_policy.json') }}"
state: present
register: step_functions_role
- name: Pause a few seconds to ensure IAM role is available to next task
pause:
seconds: 10
# ==== Tests ===================================================
- name: Create a random component for state machine name
set_fact:
random_num: "{{ 999999999 | random }}"
- name: Create a new state machine -- check_mode
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
definition: "{{ lookup('file','state_machine.json') }}"
role_arn: "{{ step_functions_role.iam_role.arn }}"
tags:
project: helloWorld
state: present
register: creation_check
check_mode: yes
- assert:
that:
- creation_check.changed == True
- creation_check.output == 'State machine would be created.'
- name: Create a new state machine
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
definition: "{{ lookup('file','state_machine.json') }}"
role_arn: "{{ step_functions_role.iam_role.arn }}"
tags:
project: helloWorld
state: present
register: creation_output
- assert:
that:
- creation_output.changed == True
- name: Pause a few seconds to ensure state machine role is available
pause:
seconds: 5
- name: Idempotent rerun of same state function -- check_mode
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
definition: "{{ lookup('file','state_machine.json') }}"
role_arn: "{{ step_functions_role.iam_role.arn }}"
tags:
project: helloWorld
state: present
register: result
check_mode: yes
- assert:
that:
- result.changed == False
- result.output == 'State is up-to-date.'
- name: Idempotent rerun of same state function
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
definition: "{{ lookup('file','state_machine.json') }}"
role_arn: "{{ step_functions_role.iam_role.arn }}"
tags:
project: helloWorld
state: present
register: result
- assert:
that:
- result.changed == False
- name: Update an existing state machine -- check_mode
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
definition: "{{ lookup('file','alternative_state_machine.json') }}"
role_arn: "{{ step_functions_role.iam_role.arn }}"
tags:
differentTag: different_tag
state: present
register: update_check
check_mode: yes
- assert:
that:
- update_check.changed == True
- "update_check.output == 'State machine would be updated: {{ creation_output.state_machine_arn }}'"
- name: Update an existing state machine
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
definition: "{{ lookup('file','alternative_state_machine.json') }}"
role_arn: "{{ step_functions_role.iam_role.arn }}"
tags:
differentTag: different_tag
state: present
register: update_output
- assert:
that:
- update_output.changed == True
- update_output.state_machine_arn == creation_output.state_machine_arn
- name: Start execution of state machine -- check_mode
aws_step_functions_state_machine_execution:
name: "{{ execution_name }}"
execution_input: "{}"
state_machine_arn: "{{ creation_output.state_machine_arn }}"
register: start_execution_output
check_mode: yes
- assert:
that:
- start_execution_output.changed == True
- "start_execution_output.output == 'State machine execution would be started.'"
- name: Start execution of state machine
aws_step_functions_state_machine_execution:
name: "{{ execution_name }}"
execution_input: "{}"
state_machine_arn: "{{ creation_output.state_machine_arn }}"
register: start_execution_output
- assert:
that:
- start_execution_output.changed
- "'execution_arn' in start_execution_output"
- "'start_date' in start_execution_output"
- name: Start execution of state machine (check for idempotency) (check mode)
aws_step_functions_state_machine_execution:
name: "{{ execution_name }}"
execution_input: "{}"
state_machine_arn: "{{ creation_output.state_machine_arn }}"
register: start_execution_output_idem_check
check_mode: yes
- assert:
that:
- not start_execution_output_idem_check.changed
- "start_execution_output_idem_check.output == 'State machine execution already exists.'"
- name: Start execution of state machine (check for idempotency)
aws_step_functions_state_machine_execution:
name: "{{ execution_name }}"
execution_input: "{}"
state_machine_arn: "{{ creation_output.state_machine_arn }}"
register: start_execution_output_idem
- assert:
that:
- not start_execution_output_idem.changed
- name: Stop execution of state machine -- check_mode
aws_step_functions_state_machine_execution:
action: stop
execution_arn: "{{ start_execution_output.execution_arn }}"
cause: "cause of the failure"
error: "error code of the failure"
register: stop_execution_output
check_mode: yes
- name: Stop execution of state machine
aws_step_functions_state_machine_execution:
action: stop
execution_arn: "{{ start_execution_output.execution_arn }}"
cause: "cause of the failure"
error: "error code of the failure"
register: stop_execution_output
- name: Stop execution of state machine (check for idempotency)
aws_step_functions_state_machine_execution:
action: stop
execution_arn: "{{ start_execution_output.execution_arn }}"
cause: "cause of the failure"
error: "error code of the failure"
register: stop_execution_output
- name: Try stopping a non-running execution -- check_mode
aws_step_functions_state_machine_execution:
action: stop
execution_arn: "{{ start_execution_output.execution_arn }}"
cause: "cause of the failure"
error: "error code of the failure"
register: stop_execution_output
check_mode: yes
- assert:
that:
- not stop_execution_output.changed
- "stop_execution_output.output == 'State machine execution is not running.'"
- name: Try stopping a non-running execution
aws_step_functions_state_machine_execution:
action: stop
execution_arn: "{{ start_execution_output.execution_arn }}"
cause: "cause of the failure"
error: "error code of the failure"
register: stop_execution_output
check_mode: yes
- assert:
that:
- not stop_execution_output.changed
- name: Start execution of state machine with the same execution name
aws_step_functions_state_machine_execution:
name: "{{ execution_name }}"
state_machine_arn: "{{ creation_output.state_machine_arn }}"
register: start_execution_output_again
- assert:
that:
- not start_execution_output_again.changed
- name: Remove state machine -- check_mode
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
state: absent
register: deletion_check
check_mode: yes
- assert:
that:
- deletion_check.changed == True
- "deletion_check.output == 'State machine would be deleted: {{ creation_output.state_machine_arn }}'"
- name: Remove state machine
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
state: absent
register: deletion_output
- assert:
that:
- deletion_output.changed == True
- deletion_output.state_machine_arn == creation_output.state_machine_arn
- name: Non-existent state machine is absent
aws_step_functions_state_machine:
name: "non_existing_state_machine"
state: absent
register: result
- assert:
that:
- result.changed == False
# ==== Cleanup ====================================================
always:
- name: Cleanup - delete state machine
aws_step_functions_state_machine:
name: "{{ state_machine_name }}"
state: absent
ignore_errors: true
- name: Cleanup - delete IAM role needed for Step Functions test
iam_role:
name: "{{ step_functions_role_name }}"
state: absent
ignore_errors: true

@ -1,3 +0,0 @@
---
dependencies:
- incidental_cs_common

@ -1,223 +0,0 @@
---
- name: setup service offering
cs_service_offering:
name: Micro
state: absent
register: so
- name: verify setup service offering
assert:
that:
- so is successful
- name: create service offering in check mode
cs_service_offering:
name: Micro
display_text: Micro 512mb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 512
host_tags: eco
storage_tags:
- eco
- backup
storage_type: local
register: so
check_mode: true
- name: verify create service offering in check mode
assert:
that:
- so is changed
- name: create service offering
cs_service_offering:
name: Micro
display_text: Micro 512mb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 512
host_tags: eco
storage_tags:
- eco
- backup
storage_type: local
register: so
- name: verify create service offering
assert:
that:
- so is changed
- so.name == "Micro"
- so.display_text == "Micro 512mb 1cpu"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: create service offering idempotence
cs_service_offering:
name: Micro
display_text: Micro 512mb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 512
host_tags: eco
storage_tags:
- eco
- backup
storage_type: local
register: so
- name: verify create service offering idempotence
assert:
that:
- so is not changed
- so.name == "Micro"
- so.display_text == "Micro 512mb 1cpu"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: update service offering in check mode
cs_service_offering:
name: Micro
display_text: Micro RAM 512MB 1vCPU
register: so
check_mode: true
- name: verify create update offering in check mode
assert:
that:
- so is changed
- so.name == "Micro"
- so.display_text == "Micro 512mb 1cpu"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: update service offering
cs_service_offering:
name: Micro
display_text: Micro RAM 512MB 1vCPU
register: so
- name: verify update service offerin
assert:
that:
- so is changed
- so.name == "Micro"
- so.display_text == "Micro RAM 512MB 1vCPU"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: update service offering idempotence
cs_service_offering:
name: Micro
display_text: Micro RAM 512MB 1vCPU
register: so
- name: verify update service offering idempotence
assert:
that:
- so is not changed
- so.name == "Micro"
- so.display_text == "Micro RAM 512MB 1vCPU"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: remove service offering in check mode
cs_service_offering:
name: Micro
state: absent
check_mode: true
register: so
- name: verify remove service offering in check mode
assert:
that:
- so is changed
- so.name == "Micro"
- so.display_text == "Micro RAM 512MB 1vCPU"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: remove service offering
cs_service_offering:
name: Micro
state: absent
register: so
- name: verify remove service offering
assert:
that:
- so is changed
- so.name == "Micro"
- so.display_text == "Micro RAM 512MB 1vCPU"
- so.cpu_number == 1
- so.cpu_speed == 2198
- so.memory == 512
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: remove service offering idempotence
cs_service_offering:
name: Micro
state: absent
register: so
- name: verify remove service offering idempotence
assert:
that:
- so is not changed
- name: create custom service offering
cs_service_offering:
name: custom
display_text: custom offer
is_customized: yes
host_tags: eco
storage_tags:
- eco
- backup
storage_type: local
register: so
- name: verify create custom service offering
assert:
that:
- so is changed
- so.name == "custom"
- so.display_text == "custom offer"
- so.is_customized == True
- so.cpu_number is not defined
- so.cpu_speed is not defined
- so.memory is not defined
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"
- name: remove custom service offering
cs_service_offering:
name: custom
state: absent
register: so
- name: verify remove service offering
assert:
that:
- so is changed
- so.name == "custom"
- so.display_text == "custom offer"
- so.host_tags == ['eco']
- so.storage_tags == ['eco', 'backup']
- so.storage_type == "local"

@ -1,3 +0,0 @@
---
- import_tasks: guest_vm_service_offering.yml
- import_tasks: system_vm_service_offering.yml

@ -1,151 +0,0 @@
---
- name: setup system offering
cs_service_offering:
name: System Offering for Ansible
is_system: true
state: absent
register: so
- name: verify setup system offering
assert:
that:
- so is successful
- name: fail missing storage type and is_system
cs_service_offering:
name: System Offering for Ansible
cpu_number: 1
cpu_speed: 500
memory: 512
host_tag: perf
storage_tag: perf
storage_type: shared
offer_ha: true
limit_cpu_usage: false
is_system: true
register: so
ignore_errors: true
- name: verify create system service offering in check mode
assert:
that:
- so is failed
- so.msg.startswith('missing required arguments:')
- name: create system service offering in check mode
cs_service_offering:
name: System Offering for Ansible
cpu_number: 1
cpu_speed: 500
memory: 512
host_tag: perf
storage_tag: perf
storage_type: shared
offer_ha: true
limit_cpu_usage: false
system_vm_type: domainrouter
is_system: true
register: so
check_mode: true
- name: verify create system service offering in check mode
assert:
that:
- so is changed
- name: create system service offering
cs_service_offering:
name: System Offering for Ansible
cpu_number: 1
cpu_speed: 500
memory: 512
host_tag: perf
storage_tag: perf
storage_type: shared
offer_ha: true
limit_cpu_usage: false
system_vm_type: domainrouter
is_system: true
register: so
- name: verify create system service offering
assert:
that:
- so is changed
- so.name == "System Offering for Ansible"
- so.display_text == "System Offering for Ansible"
- so.cpu_number == 1
- so.cpu_speed == 500
- so.memory == 512
- so.host_tags == ['perf']
- so.storage_tags == ['perf']
- so.storage_type == "shared"
- so.offer_ha == true
- so.limit_cpu_usage == false
- so.system_vm_type == "domainrouter"
- so.is_system == true
- name: create system service offering idempotence
cs_service_offering:
name: System Offering for Ansible
cpu_number: 1
cpu_speed: 500
memory: 512
host_tag: perf
storage_tag: perf
storage_type: shared
offer_ha: true
limit_cpu_usage: false
system_vm_type: domainrouter
is_system: true
register: so
- name: verify create system service offering idempotence
assert:
that:
- so is not changed
- so.name == "System Offering for Ansible"
- so.display_text == "System Offering for Ansible"
- so.cpu_number == 1
- so.cpu_speed == 500
- so.memory == 512
- so.host_tags == ['perf']
- so.storage_tags == ['perf']
- so.storage_type == "shared"
- so.offer_ha == true
- so.limit_cpu_usage == false
- so.system_vm_type == "domainrouter"
- so.is_system == true
- name: remove system service offering in check mode
cs_service_offering:
name: System Offering for Ansible
is_system: true
state: absent
check_mode: true
register: so
- name: verify remove system service offering in check mode
assert:
that:
- so is changed
- so.name == "System Offering for Ansible"
- so.is_system == true
- name: remove system service offering
cs_service_offering:
name: System Offering for Ansible
is_system: true
state: absent
register: so
- name: verify remove system service offering
assert:
that:
- so is changed
- so.name == "System Offering for Ansible"
- so.is_system == true
- name: remove system service offering idempotence
cs_service_offering:
name: System Offering for Ansible
is_system: true
state: absent
register: so
- name: verify remove system service offering idempotence
assert:
that:
- so is not changed

@ -1,232 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2019, Tom De Keyser (@tdekeyser)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: aws_step_functions_state_machine
short_description: Manage AWS Step Functions state machines
version_added: "2.10"
description:
- Create, update and delete state machines in AWS Step Functions.
- Calling the module in C(state=present) for an existing AWS Step Functions state machine
will attempt to update the state machine definition, IAM Role, or tags with the provided data.
options:
name:
description:
- Name of the state machine
required: true
type: str
definition:
description:
- The Amazon States Language definition of the state machine. See
U(https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) for more
information on the Amazon States Language.
- "This parameter is required when C(state=present)."
type: json
role_arn:
description:
- The ARN of the IAM Role that will be used by the state machine for its executions.
- "This parameter is required when C(state=present)."
type: str
state:
description:
- Desired state for the state machine
default: present
choices: [ present, absent ]
type: str
tags:
description:
- A hash/dictionary of tags to add to the new state machine or to add/remove from an existing one.
type: dict
purge_tags:
description:
- If yes, existing tags will be purged from the resource to match exactly what is defined by I(tags) parameter.
If the I(tags) parameter is not set then tags will not be modified.
default: yes
type: bool
extends_documentation_fragment:
- aws
- ec2
author:
- Tom De Keyser (@tdekeyser)
'''
EXAMPLES = '''
# Create a new AWS Step Functions state machine
- name: Setup HelloWorld state machine
aws_step_functions_state_machine:
name: "HelloWorldStateMachine"
definition: "{{ lookup('file','state_machine.json') }}"
role_arn: arn:aws:iam::987654321012:role/service-role/invokeLambdaStepFunctionsRole
tags:
project: helloWorld
# Update an existing state machine
- name: Change IAM Role and tags of HelloWorld state machine
aws_step_functions_state_machine:
name: HelloWorldStateMachine
definition: "{{ lookup('file','state_machine.json') }}"
role_arn: arn:aws:iam::987654321012:role/service-role/anotherStepFunctionsRole
tags:
otherTag: aDifferentTag
# Remove the AWS Step Functions state machine
- name: Delete HelloWorld state machine
aws_step_functions_state_machine:
name: HelloWorldStateMachine
state: absent
'''
RETURN = '''
state_machine_arn:
description: ARN of the AWS Step Functions state machine
type: str
returned: always
'''
from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import ansible_dict_to_boto3_tag_list, AWSRetry, compare_aws_tags, boto3_tag_list_to_ansible_dict
try:
from botocore.exceptions import ClientError, BotoCoreError
except ImportError:
pass # caught by AnsibleAWSModule
def manage_state_machine(state, sfn_client, module):
state_machine_arn = get_state_machine_arn(sfn_client, module)
if state == 'present':
if state_machine_arn is None:
create(sfn_client, module)
else:
update(state_machine_arn, sfn_client, module)
elif state == 'absent':
if state_machine_arn is not None:
remove(state_machine_arn, sfn_client, module)
check_mode(module, msg='State is up-to-date.')
module.exit_json(changed=False)
def create(sfn_client, module):
check_mode(module, msg='State machine would be created.', changed=True)
tags = module.params.get('tags')
sfn_tags = ansible_dict_to_boto3_tag_list(tags, tag_name_key_name='key', tag_value_key_name='value') if tags else []
state_machine = sfn_client.create_state_machine(
name=module.params.get('name'),
definition=module.params.get('definition'),
roleArn=module.params.get('role_arn'),
tags=sfn_tags
)
module.exit_json(changed=True, state_machine_arn=state_machine.get('stateMachineArn'))
def remove(state_machine_arn, sfn_client, module):
check_mode(module, msg='State machine would be deleted: {0}'.format(state_machine_arn), changed=True)
sfn_client.delete_state_machine(stateMachineArn=state_machine_arn)
module.exit_json(changed=True, state_machine_arn=state_machine_arn)
def update(state_machine_arn, sfn_client, module):
tags_to_add, tags_to_remove = compare_tags(state_machine_arn, sfn_client, module)
if params_changed(state_machine_arn, sfn_client, module) or tags_to_add or tags_to_remove:
check_mode(module, msg='State machine would be updated: {0}'.format(state_machine_arn), changed=True)
sfn_client.update_state_machine(
stateMachineArn=state_machine_arn,
definition=module.params.get('definition'),
roleArn=module.params.get('role_arn')
)
sfn_client.untag_resource(
resourceArn=state_machine_arn,
tagKeys=tags_to_remove
)
sfn_client.tag_resource(
resourceArn=state_machine_arn,
tags=ansible_dict_to_boto3_tag_list(tags_to_add, tag_name_key_name='key', tag_value_key_name='value')
)
module.exit_json(changed=True, state_machine_arn=state_machine_arn)
def compare_tags(state_machine_arn, sfn_client, module):
new_tags = module.params.get('tags')
current_tags = sfn_client.list_tags_for_resource(resourceArn=state_machine_arn).get('tags')
return compare_aws_tags(boto3_tag_list_to_ansible_dict(current_tags), new_tags if new_tags else {}, module.params.get('purge_tags'))
def params_changed(state_machine_arn, sfn_client, module):
"""
Check whether the state machine definition or IAM Role ARN is different
from the existing state machine parameters.
"""
current = sfn_client.describe_state_machine(stateMachineArn=state_machine_arn)
return current.get('definition') != module.params.get('definition') or current.get('roleArn') != module.params.get('role_arn')
def get_state_machine_arn(sfn_client, module):
"""
Finds the state machine ARN based on the name parameter. Returns None if
there is no state machine with this name.
"""
target_name = module.params.get('name')
all_state_machines = sfn_client.list_state_machines(aws_retry=True).get('stateMachines')
for state_machine in all_state_machines:
if state_machine.get('name') == target_name:
return state_machine.get('stateMachineArn')
def check_mode(module, msg='', changed=False):
if module.check_mode:
module.exit_json(changed=changed, output=msg)
def main():
module_args = dict(
name=dict(type='str', required=True),
definition=dict(type='json'),
role_arn=dict(type='str'),
state=dict(choices=['present', 'absent'], default='present'),
tags=dict(default=None, type='dict'),
purge_tags=dict(default=True, type='bool'),
)
module = AnsibleAWSModule(
argument_spec=module_args,
required_if=[('state', 'present', ['role_arn']), ('state', 'present', ['definition'])],
supports_check_mode=True
)
sfn_client = module.client('stepfunctions', retry_decorator=AWSRetry.jittered_backoff(retries=5))
state = module.params.get('state')
try:
manage_state_machine(state, sfn_client, module)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg='Failed to manage state machine')
if __name__ == '__main__':
main()

@ -1,197 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2019, Prasad Katti (@prasadkatti)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: aws_step_functions_state_machine_execution
short_description: Start or stop execution of an AWS Step Functions state machine.
version_added: "2.10"
description:
- Start or stop execution of a state machine in AWS Step Functions.
options:
action:
description: Desired action (start or stop) for a state machine execution.
default: start
choices: [ start, stop ]
type: str
name:
description: Name of the execution.
type: str
execution_input:
description: The JSON input data for the execution.
type: json
default: {}
state_machine_arn:
description: The ARN of the state machine that will be executed.
type: str
execution_arn:
description: The ARN of the execution you wish to stop.
type: str
cause:
description: A detailed explanation of the cause for stopping the execution.
type: str
default: ''
error:
description: The error code of the failure to pass in when stopping the execution.
type: str
default: ''
extends_documentation_fragment:
- aws
- ec2
author:
- Prasad Katti (@prasadkatti)
'''
EXAMPLES = '''
- name: Start an execution of a state machine
aws_step_functions_state_machine_execution:
name: an_execution_name
execution_input: '{ "IsHelloWorldExample": true }'
state_machine_arn: "arn:aws:states:us-west-2:682285639423:stateMachine:HelloWorldStateMachine"
- name: Stop an execution of a state machine
aws_step_functions_state_machine_execution:
action: stop
execution_arn: "arn:aws:states:us-west-2:682285639423:execution:HelloWorldStateMachineCopy:a1e8e2b5-5dfe-d40e-d9e3-6201061047c8"
cause: "cause of task failure"
error: "error code of the failure"
'''
RETURN = '''
execution_arn:
description: ARN of the AWS Step Functions state machine execution.
type: str
returned: if action == start and changed == True
sample: "arn:aws:states:us-west-2:682285639423:execution:HelloWorldStateMachineCopy:a1e8e2b5-5dfe-d40e-d9e3-6201061047c8"
start_date:
description: The date the execution is started.
type: str
returned: if action == start and changed == True
sample: "2019-11-02T22:39:49.071000-07:00"
stop_date:
description: The date the execution is stopped.
type: str
returned: if action == stop
sample: "2019-11-02T22:39:49.071000-07:00"
'''
from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import camel_dict_to_snake_dict
try:
from botocore.exceptions import ClientError, BotoCoreError
except ImportError:
pass # caught by AnsibleAWSModule
def start_execution(module, sfn_client):
'''
start_execution uses execution name to determine if a previous execution already exists.
If an execution by the provided name exists, call client.start_execution will not be called.
'''
state_machine_arn = module.params.get('state_machine_arn')
name = module.params.get('name')
execution_input = module.params.get('execution_input')
try:
# list_executions is eventually consistent
page_iterators = sfn_client.get_paginator('list_executions').paginate(stateMachineArn=state_machine_arn)
for execution in page_iterators.build_full_result()['executions']:
if name == execution['name']:
check_mode(module, msg='State machine execution already exists.', changed=False)
module.exit_json(changed=False)
check_mode(module, msg='State machine execution would be started.', changed=True)
res_execution = sfn_client.start_execution(
stateMachineArn=state_machine_arn,
name=name,
input=execution_input
)
except (ClientError, BotoCoreError) as e:
if e.response['Error']['Code'] == 'ExecutionAlreadyExists':
# this will never be executed anymore
module.exit_json(changed=False)
module.fail_json_aws(e, msg="Failed to start execution.")
module.exit_json(changed=True, **camel_dict_to_snake_dict(res_execution))
def stop_execution(module, sfn_client):
cause = module.params.get('cause')
error = module.params.get('error')
execution_arn = module.params.get('execution_arn')
try:
# describe_execution is eventually consistent
execution_status = sfn_client.describe_execution(executionArn=execution_arn)['status']
if execution_status != 'RUNNING':
check_mode(module, msg='State machine execution is not running.', changed=False)
module.exit_json(changed=False)
check_mode(module, msg='State machine execution would be stopped.', changed=True)
res = sfn_client.stop_execution(
executionArn=execution_arn,
cause=cause,
error=error
)
except (ClientError, BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to stop execution.")
module.exit_json(changed=True, **camel_dict_to_snake_dict(res))
def check_mode(module, msg='', changed=False):
if module.check_mode:
module.exit_json(changed=changed, output=msg)
def main():
module_args = dict(
action=dict(choices=['start', 'stop'], default='start'),
name=dict(type='str'),
execution_input=dict(type='json', default={}),
state_machine_arn=dict(type='str'),
cause=dict(type='str', default=''),
error=dict(type='str', default=''),
execution_arn=dict(type='str')
)
module = AnsibleAWSModule(
argument_spec=module_args,
required_if=[('action', 'start', ['name', 'state_machine_arn']),
('action', 'stop', ['execution_arn']),
],
supports_check_mode=True
)
sfn_client = module.client('stepfunctions')
action = module.params.get('action')
if action == "start":
start_execution(module, sfn_client)
else:
stop_execution(module, sfn_client)
if __name__ == '__main__':
main()

@ -1,583 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_service_offering
description:
- Create and delete service offerings for guest and system VMs.
- Update display_text of existing service offering.
short_description: Manages service offerings on Apache CloudStack based clouds.
version_added: '2.5'
author: René Moser (@resmo)
options:
disk_bytes_read_rate:
description:
- Bytes read rate of the disk offering.
type: int
aliases: [ bytes_read_rate ]
disk_bytes_write_rate:
description:
- Bytes write rate of the disk offering.
type: int
aliases: [ bytes_write_rate ]
cpu_number:
description:
- The number of CPUs of the service offering.
type: int
cpu_speed:
description:
- The CPU speed of the service offering in MHz.
type: int
limit_cpu_usage:
description:
- Restrict the CPU usage to committed service offering.
type: bool
deployment_planner:
description:
- The deployment planner heuristics used to deploy a VM of this offering.
- If not set, the value of global config I(vm.deployment.planner) is used.
type: str
display_text:
description:
- Display text of the service offering.
- If not set, I(name) will be used as I(display_text) while creating.
type: str
domain:
description:
- Domain the service offering is related to.
- Public for all domains and subdomains if not set.
type: str
host_tags:
description:
- The host tags for this service offering.
type: list
aliases:
- host_tag
hypervisor_snapshot_reserve:
description:
- Hypervisor snapshot reserve space as a percent of a volume.
- Only for managed storage using Xen or VMware.
type: int
is_iops_customized:
description:
- Whether compute offering iops is custom or not.
type: bool
aliases: [ disk_iops_customized ]
disk_iops_read_rate:
description:
- IO requests read rate of the disk offering.
type: int
disk_iops_write_rate:
description:
- IO requests write rate of the disk offering.
type: int
disk_iops_max:
description:
- Max. iops of the compute offering.
type: int
disk_iops_min:
description:
- Min. iops of the compute offering.
type: int
is_system:
description:
- Whether it is a system VM offering or not.
type: bool
default: no
is_volatile:
description:
- Whether the virtual machine needs to be volatile or not.
- Every reboot of VM the root disk is detached then destroyed and a fresh root disk is created and attached to VM.
type: bool
memory:
description:
- The total memory of the service offering in MB.
type: int
name:
description:
- Name of the service offering.
type: str
required: true
network_rate:
description:
- Data transfer rate in Mb/s allowed.
- Supported only for non-system offering and system offerings having I(system_vm_type=domainrouter).
type: int
offer_ha:
description:
- Whether HA is set for the service offering.
type: bool
default: no
provisioning_type:
description:
- Provisioning type used to create volumes.
type: str
choices:
- thin
- sparse
- fat
service_offering_details:
description:
- Details for planner, used to store specific parameters.
- A list of dictionaries having keys C(key) and C(value).
type: list
state:
description:
- State of the service offering.
type: str
choices:
- present
- absent
default: present
storage_type:
description:
- The storage type of the service offering.
type: str
choices:
- local
- shared
system_vm_type:
description:
- The system VM type.
- Required if I(is_system=yes).
type: str
choices:
- domainrouter
- consoleproxy
- secondarystoragevm
storage_tags:
description:
- The storage tags for this service offering.
type: list
aliases:
- storage_tag
is_customized:
description:
- Whether the offering is customizable or not.
type: bool
version_added: '2.8'
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
- name: Create a non-volatile compute service offering with local storage
cs_service_offering:
name: Micro
display_text: Micro 512mb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 512
host_tags: eco
storage_type: local
delegate_to: localhost
- name: Create a volatile compute service offering with shared storage
cs_service_offering:
name: Tiny
display_text: Tiny 1gb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 1024
storage_type: shared
is_volatile: yes
host_tags: eco
storage_tags: eco
delegate_to: localhost
- name: Create or update a volatile compute service offering with shared storage
cs_service_offering:
name: Tiny
display_text: Tiny 1gb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 1024
storage_type: shared
is_volatile: yes
host_tags: eco
storage_tags: eco
delegate_to: localhost
- name: Create or update a custom compute service offering
cs_service_offering:
name: custom
display_text: custom compute offer
is_customized: yes
storage_type: shared
host_tags: eco
storage_tags: eco
delegate_to: localhost
- name: Remove a compute service offering
cs_service_offering:
name: Tiny
state: absent
delegate_to: localhost
- name: Create or update a system offering for the console proxy
cs_service_offering:
name: System Offering for Console Proxy 2GB
display_text: System Offering for Console Proxy 2GB RAM
is_system: yes
system_vm_type: consoleproxy
cpu_number: 1
cpu_speed: 2198
memory: 2048
storage_type: shared
storage_tags: perf
delegate_to: localhost
- name: Remove a system offering
cs_service_offering:
name: System Offering for Console Proxy 2GB
is_system: yes
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the service offering
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
cpu_number:
description: Number of CPUs in the service offering
returned: success
type: int
sample: 4
cpu_speed:
description: Speed of CPUs in MHz in the service offering
returned: success
type: int
sample: 2198
disk_iops_max:
description: Max iops of the disk offering
returned: success
type: int
sample: 1000
disk_iops_min:
description: Min iops of the disk offering
returned: success
type: int
sample: 500
disk_bytes_read_rate:
description: Bytes read rate of the service offering
returned: success
type: int
sample: 1000
disk_bytes_write_rate:
description: Bytes write rate of the service offering
returned: success
type: int
sample: 1000
disk_iops_read_rate:
description: IO requests per second read rate of the service offering
returned: success
type: int
sample: 1000
disk_iops_write_rate:
description: IO requests per second write rate of the service offering
returned: success
type: int
sample: 1000
created:
description: Date the offering was created
returned: success
type: str
sample: 2017-11-19T10:48:59+0000
display_text:
description: Display text of the offering
returned: success
type: str
sample: Micro 512mb 1cpu
domain:
description: Domain the offering is into
returned: success
type: str
sample: ROOT
host_tags:
description: List of host tags
returned: success
type: list
sample: [ 'eco' ]
storage_tags:
description: List of storage tags
returned: success
type: list
sample: [ 'eco' ]
is_system:
description: Whether the offering is for system VMs or not
returned: success
type: bool
sample: false
is_iops_customized:
description: Whether the offering uses custom IOPS or not
returned: success
type: bool
sample: false
is_volatile:
description: Whether the offering is volatile or not
returned: success
type: bool
sample: false
limit_cpu_usage:
description: Whether the CPU usage is restricted to committed service offering
returned: success
type: bool
sample: false
memory:
description: Memory of the system offering
returned: success
type: int
sample: 512
name:
description: Name of the system offering
returned: success
type: str
sample: Micro
offer_ha:
description: Whether HA support is enabled in the offering or not
returned: success
type: bool
sample: false
provisioning_type:
description: Provisioning type used to create volumes
returned: success
type: str
sample: thin
storage_type:
description: Storage type used to create volumes
returned: success
type: str
sample: shared
system_vm_type:
description: System VM type of this offering
returned: success
type: str
sample: consoleproxy
service_offering_details:
description: Additioanl service offering details
returned: success
type: dict
sample: "{'vgpuType': 'GRID K180Q','pciDevice':'Group of NVIDIA Corporation GK107GL [GRID K1] GPUs'}"
network_rate:
description: Data transfer rate in megabits per second allowed
returned: success
type: int
sample: 1000
is_customized:
description: Whether the offering is customizable or not
returned: success
type: bool
sample: false
version_added: '2.8'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackServiceOffering(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackServiceOffering, self).__init__(module)
self.returns = {
'cpunumber': 'cpu_number',
'cpuspeed': 'cpu_speed',
'deploymentplanner': 'deployment_planner',
'diskBytesReadRate': 'disk_bytes_read_rate',
'diskBytesWriteRate': 'disk_bytes_write_rate',
'diskIopsReadRate': 'disk_iops_read_rate',
'diskIopsWriteRate': 'disk_iops_write_rate',
'maxiops': 'disk_iops_max',
'miniops': 'disk_iops_min',
'hypervisorsnapshotreserve': 'hypervisor_snapshot_reserve',
'iscustomized': 'is_customized',
'iscustomizediops': 'is_iops_customized',
'issystem': 'is_system',
'isvolatile': 'is_volatile',
'limitcpuuse': 'limit_cpu_usage',
'memory': 'memory',
'networkrate': 'network_rate',
'offerha': 'offer_ha',
'provisioningtype': 'provisioning_type',
'serviceofferingdetails': 'service_offering_details',
'storagetype': 'storage_type',
'systemvmtype': 'system_vm_type',
'tags': 'storage_tags',
}
def get_service_offering(self):
args = {
'name': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
'issystem': self.module.params.get('is_system'),
'systemvmtype': self.module.params.get('system_vm_type'),
}
service_offerings = self.query_api('listServiceOfferings', **args)
if service_offerings:
return service_offerings['serviceoffering'][0]
def present_service_offering(self):
service_offering = self.get_service_offering()
if not service_offering:
service_offering = self._create_offering(service_offering)
else:
service_offering = self._update_offering(service_offering)
return service_offering
def absent_service_offering(self):
service_offering = self.get_service_offering()
if service_offering:
self.result['changed'] = True
if not self.module.check_mode:
args = {
'id': service_offering['id'],
}
self.query_api('deleteServiceOffering', **args)
return service_offering
def _create_offering(self, service_offering):
self.result['changed'] = True
system_vm_type = self.module.params.get('system_vm_type')
is_system = self.module.params.get('is_system')
required_params = []
if is_system and not system_vm_type:
required_params.append('system_vm_type')
self.module.fail_on_missing_params(required_params=required_params)
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'bytesreadrate': self.module.params.get('disk_bytes_read_rate'),
'byteswriterate': self.module.params.get('disk_bytes_write_rate'),
'cpunumber': self.module.params.get('cpu_number'),
'cpuspeed': self.module.params.get('cpu_speed'),
'customizediops': self.module.params.get('is_iops_customized'),
'deploymentplanner': self.module.params.get('deployment_planner'),
'domainid': self.get_domain(key='id'),
'hosttags': self.module.params.get('host_tags'),
'hypervisorsnapshotreserve': self.module.params.get('hypervisor_snapshot_reserve'),
'iopsreadrate': self.module.params.get('disk_iops_read_rate'),
'iopswriterate': self.module.params.get('disk_iops_write_rate'),
'maxiops': self.module.params.get('disk_iops_max'),
'miniops': self.module.params.get('disk_iops_min'),
'issystem': is_system,
'isvolatile': self.module.params.get('is_volatile'),
'memory': self.module.params.get('memory'),
'networkrate': self.module.params.get('network_rate'),
'offerha': self.module.params.get('offer_ha'),
'provisioningtype': self.module.params.get('provisioning_type'),
'serviceofferingdetails': self.module.params.get('service_offering_details'),
'storagetype': self.module.params.get('storage_type'),
'systemvmtype': system_vm_type,
'tags': self.module.params.get('storage_tags'),
'limitcpuuse': self.module.params.get('limit_cpu_usage'),
'customized': self.module.params.get('is_customized')
}
if not self.module.check_mode:
res = self.query_api('createServiceOffering', **args)
service_offering = res['serviceoffering']
return service_offering
def _update_offering(self, service_offering):
args = {
'id': service_offering['id'],
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
}
if self.has_changed(args, service_offering):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateServiceOffering', **args)
service_offering = res['serviceoffering']
return service_offering
def get_result(self, service_offering):
super(AnsibleCloudStackServiceOffering, self).get_result(service_offering)
if service_offering:
if 'hosttags' in service_offering:
self.result['host_tags'] = service_offering['hosttags'].split(',') or [service_offering['hosttags']]
# Prevent confusion, the api returns a tags key for storage tags.
if 'tags' in service_offering:
self.result['storage_tags'] = service_offering['tags'].split(',') or [service_offering['tags']]
if 'tags' in self.result:
del self.result['tags']
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
cpu_number=dict(type='int'),
cpu_speed=dict(type='int'),
limit_cpu_usage=dict(type='bool'),
deployment_planner=dict(),
domain=dict(),
host_tags=dict(type='list', aliases=['host_tag']),
hypervisor_snapshot_reserve=dict(type='int'),
disk_bytes_read_rate=dict(type='int', aliases=['bytes_read_rate']),
disk_bytes_write_rate=dict(type='int', aliases=['bytes_write_rate']),
disk_iops_read_rate=dict(type='int'),
disk_iops_write_rate=dict(type='int'),
disk_iops_max=dict(type='int'),
disk_iops_min=dict(type='int'),
is_system=dict(type='bool', default=False),
is_volatile=dict(type='bool'),
is_iops_customized=dict(type='bool', aliases=['disk_iops_customized']),
memory=dict(type='int'),
network_rate=dict(type='int'),
offer_ha=dict(type='bool'),
provisioning_type=dict(choices=['thin', 'sparse', 'fat']),
service_offering_details=dict(type='list'),
storage_type=dict(choices=['local', 'shared']),
system_vm_type=dict(choices=['domainrouter', 'consoleproxy', 'secondarystoragevm']),
storage_tags=dict(type='list', aliases=['storage_tag']),
state=dict(choices=['present', 'absent'], default='present'),
is_customized=dict(type='bool'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_so = AnsibleCloudStackServiceOffering(module)
state = module.params.get('state')
if state == "absent":
service_offering = acs_so.absent_service_offering()
else:
service_offering = acs_so.present_service_offering()
result = acs_so.get_result(service_offering)
module.exit_json(**result)
if __name__ == '__main__':
main()
Loading…
Cancel
Save