diff --git a/plugins/modules/ec2_placement_group.py b/plugins/modules/ec2_placement_group.py new file mode 100644 index 00000000000..d1d26535261 --- /dev/null +++ b/plugins/modules/ec2_placement_group.py @@ -0,0 +1,209 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# 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: ec2_placement_group +short_description: Create or delete an EC2 Placement Group +description: + - Create an EC2 Placement Group; if the placement group already exists, + nothing is done. Or, delete an existing placement group. If the placement + group is absent, do nothing. See also + U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html) +author: "Brad Macpherson (@iiibrad)" +options: + name: + description: + - The name for the placement group. + required: true + type: str + state: + description: + - Create or delete placement group. + default: present + choices: [ 'present', 'absent' ] + type: str + strategy: + description: + - Placement group strategy. Cluster will cluster instances into a + low-latency group in a single Availability Zone, while Spread spreads + instances across underlying hardware. + default: cluster + choices: [ 'cluster', 'spread' ] + type: str +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide +# for details. + +# Create a placement group. +- ec2_placement_group: + name: my-cluster + state: present + +# Create a Spread placement group. +- ec2_placement_group: + name: my-cluster + state: present + strategy: spread + +# Delete a placement group. +- ec2_placement_group: + name: my-cluster + state: absent + +''' + + +RETURN = ''' +placement_group: + description: Placement group attributes + returned: when state != absent + type: complex + contains: + name: + description: PG name + type: str + sample: my-cluster + state: + description: PG state + type: str + sample: "available" + strategy: + description: PG strategy + type: str + sample: "cluster" + +''' + +from ansible_collections.ansible.amazon.plugins.module_utils.aws.core import AnsibleAWSModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import AWSRetry +try: + from botocore.exceptions import (BotoCoreError, ClientError) +except ImportError: + pass # caught by AnsibleAWSModule + + +@AWSRetry.exponential_backoff() +def get_placement_group_details(connection, module): + name = module.params.get("name") + try: + response = connection.describe_placement_groups( + Filters=[{ + "Name": "group-name", + "Values": [name] + }]) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws( + e, + msg="Couldn't find placement group named [%s]" % name) + + if len(response['PlacementGroups']) != 1: + return None + else: + placement_group = response['PlacementGroups'][0] + return { + "name": placement_group['GroupName'], + "state": placement_group['State'], + "strategy": placement_group['Strategy'], + } + + +@AWSRetry.exponential_backoff() +def create_placement_group(connection, module): + name = module.params.get("name") + strategy = module.params.get("strategy") + + try: + connection.create_placement_group( + GroupName=name, Strategy=strategy, DryRun=module.check_mode) + except (BotoCoreError, ClientError) as e: + if e.response['Error']['Code'] == "DryRunOperation": + module.exit_json(changed=True, placement_group={ + "name": name, + "state": 'DryRun', + "strategy": strategy, + }) + module.fail_json_aws( + e, + msg="Couldn't create placement group [%s]" % name) + + module.exit_json(changed=True, + placement_group=get_placement_group_details( + connection, module + )) + + +@AWSRetry.exponential_backoff() +def delete_placement_group(connection, module): + name = module.params.get("name") + + try: + connection.delete_placement_group( + GroupName=name, DryRun=module.check_mode) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws( + e, + msg="Couldn't delete placement group [%s]" % name) + + module.exit_json(changed=True) + + +def main(): + argument_spec = dict( + name=dict(required=True, type='str'), + state=dict(default='present', choices=['present', 'absent']), + strategy=dict(default='cluster', choices=['cluster', 'spread']) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + connection = module.client('ec2') + + state = module.params.get("state") + + if state == 'present': + placement_group = get_placement_group_details(connection, module) + if placement_group is None: + create_placement_group(connection, module) + else: + strategy = module.params.get("strategy") + if placement_group['strategy'] == strategy: + module.exit_json( + changed=False, placement_group=placement_group) + else: + name = module.params.get("name") + module.fail_json( + msg=("Placement group '{}' exists, can't change strategy" + + " from '{}' to '{}'").format( + name, + placement_group['strategy'], + strategy)) + + elif state == 'absent': + placement_group = get_placement_group_details(connection, module) + if placement_group is None: + module.exit_json(changed=False) + else: + delete_placement_group(connection, module) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/ec2_placement_group_facts.py b/plugins/modules/ec2_placement_group_facts.py new file mode 120000 index 00000000000..7d33ef0167f --- /dev/null +++ b/plugins/modules/ec2_placement_group_facts.py @@ -0,0 +1 @@ +ec2_placement_group_info.py \ No newline at end of file diff --git a/plugins/modules/ec2_placement_group_info.py b/plugins/modules/ec2_placement_group_info.py new file mode 100644 index 00000000000..f0e7092e43e --- /dev/null +++ b/plugins/modules/ec2_placement_group_info.py @@ -0,0 +1,129 @@ +#!/usr/bin/python +# Copyright (c) 2017 Ansible Project +# 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: ec2_placement_group_info +short_description: List EC2 Placement Group(s) details +description: + - List details of EC2 Placement Group(s). + - This module was called C(ec2_placement_group_facts) before Ansible 2.9. The usage did not change. +author: "Brad Macpherson (@iiibrad)" +options: + names: + description: + - A list of names to filter on. If a listed group does not exist, there + will be no corresponding entry in the result; no error will be raised. + type: list + elements: str + required: false + default: [] +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details or the AWS region, +# see the AWS Guide for details. + +# List all placement groups. +- ec2_placement_group_info: + register: all_ec2_placement_groups + +# List two placement groups. +- ec2_placement_group_info: + names: + - my-cluster + - my-other-cluster + register: specific_ec2_placement_groups + +- debug: msg="{{ specific_ec2_placement_groups | json_query(\"[?name=='my-cluster']\") }}" + +''' + + +RETURN = ''' +placement_groups: + description: Placement group attributes + returned: always + type: complex + contains: + name: + description: PG name + type: str + sample: my-cluster + state: + description: PG state + type: str + sample: "available" + strategy: + description: PG strategy + type: str + sample: "cluster" + +''' + +from ansible_collections.ansible.amazon.plugins.module_utils.aws.core import AnsibleAWSModule +try: + from botocore.exceptions import (BotoCoreError, ClientError) +except ImportError: + pass # caught by AnsibleAWSModule + + +def get_placement_groups_details(connection, module): + names = module.params.get("names") + try: + if len(names) > 0: + response = connection.describe_placement_groups( + Filters=[{ + "Name": "group-name", + "Values": names + }]) + else: + response = connection.describe_placement_groups() + except (BotoCoreError, ClientError) as e: + module.fail_json_aws( + e, + msg="Couldn't find placement groups named [%s]" % names) + + results = [] + for placement_group in response['PlacementGroups']: + results.append({ + "name": placement_group['GroupName'], + "state": placement_group['State'], + "strategy": placement_group['Strategy'], + }) + return results + + +def main(): + argument_spec = dict( + names=dict(type='list', default=[]) + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + if module._module._name == 'ec2_placement_group_facts': + module._module.deprecate("The 'ec2_placement_group_facts' module has been renamed to 'ec2_placement_group_info'", version='2.13') + + connection = module.client('ec2') + + placement_groups = get_placement_groups_details(connection, module) + module.exit_json(changed=False, placement_groups=placement_groups) + + +if __name__ == '__main__': + main()