Skip to content

Commit

Permalink
ec2_spot_instance_info: add new module for describing spot instance r…
Browse files Browse the repository at this point in the history
…equests (#487)

ec2_spot_instance_info: add new module for describing spot instance requests

SUMMARY

Added a new module that describes the specified Spot Instance requests.

ISSUE TYPE


New Module Pull Request

COMPONENT NAME

ec2_spot_instance_info
ADDITIONAL INFORMATION



Related Documentation:
https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15/DescribeSpotInstanceRequests

Reviewed-by: Jill R <None>
Reviewed-by: None <None>
  • Loading branch information
mandar242 authored Sep 13, 2021
1 parent cf7721a commit ccb1760
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- ec2_spot_instance_info - Added a new module that describes the specified Spot Instance requests (https://github.com/ansible-collections/amazon.aws/pull/487).
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ action_groups:
- ec2_snapshot
- ec2_snapshot_info
- ec2_spot_instance
- ec2_spot_instance_info
- ec2_tag
- ec2_tag_info
- ec2_vol
Expand Down
168 changes: 168 additions & 0 deletions plugins/modules/ec2_spot_instance_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/python
# This file is part of Ansible
# GNU General Public License v3.0+ (see COPYING or https://wwww.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = '''
---
module: ec2_spot_instance_info
version_added: 2.0.0
short_description: Gather information about ec2 spot instance requests
description:
- Describes the specified Spot Instance requests.
author:
- Mandar Vijay Kulkarni (@mandar242)
options:
filters:
description:
- A dict of filters to apply. Each dict item consists of a filter key and a filter value.
- Filter names and values are case sensitive.
- See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSpotInstanceRequests.html) for possible filters.
required: false
default: {}
type: dict
spot_instance_request_ids:
description:
- One or more Spot Instance request IDs.
required: false
type: list
elements: str
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
'''

EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.
- name: describe the Spot Instance requests based on request IDs
amazon.aws.ec2_spot_instance_info:
spot_instance_request_ids:
- sir-12345678
- name: describe the Spot Instance requests and filter results based on instance type
amazon.aws.ec2_spot_instance_info:
spot_instance_request_ids:
- sir-12345678
- sir-13579246
- sir-87654321
filters:
launch.instance-type: t3.medium
- name: describe the Spot requests filtered using multiple filters
amazon.aws.ec2_spot_instance_info:
filters:
state: active
launch.block-device-mapping.device-name: /dev/sdb
'''

RETURN = '''
spot_request:
description: The gathered information about specified spot instance requests.
returned: when success
type: dict
sample: {
"create_time": "2021-09-01T21:05:57+00:00",
"instance_id": "i-08877936b801ac475",
"instance_interruption_behavior": "terminate",
"launch_specification": {
"ebs_optimized": false,
"image_id": "ami-0443305dabd4be2bc",
"instance_type": "t2.medium",
"key_name": "zuul",
"monitoring": {
"enabled": false
},
"placement": {
"availability_zone": "us-east-2b"
},
"security_groups": [
{
"group_id": "sg-01f9833207d53b937",
"group_name": "default"
}
],
"subnet_id": "subnet-07d906b8358869bda"
},
"launched_availability_zone": "us-east-2b",
"product_description": "Linux/UNIX",
"spot_instance_request_id": "sir-c3cp9jsk",
"spot_price": "0.046400",
"state": "active",
"status": {
"code": "fulfilled",
"message": "Your spot request is fulfilled.",
"update_time": "2021-09-01T21:05:59+00:00"
},
"tags": {},
"type": "one-time",
"valid_until": "2021-09-08T21:05:57+00:00"
}
'''


try:
import botocore
except ImportError:
pass # Handled by AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list


def _describe_spot_instance_requests(connection, **params):
paginator = connection.get_paginator('describe_spot_instance_requests')
return paginator.paginate(**params).build_full_result()


def describe_spot_instance_requests(connection, module):

params = {}

if module.params.get('filters'):
params['Filters'] = ansible_dict_to_boto3_filter_list(module.params.get('filters'))
if module.params.get('spot_instance_request_ids'):
params['SpotInstanceRequestIds'] = module.params.get('spot_instance_request_ids')

try:
describe_spot_instance_requests_response = _describe_spot_instance_requests(connection, **params)['SpotInstanceRequests']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Failed to describe spot instance requests')

spot_request = []
for response_list_item in describe_spot_instance_requests_response:
spot_request.append(camel_dict_to_snake_dict(response_list_item))

if len(spot_request) == 0:
module.exit_json(msg='No spot requests found for specified options')

module.exit_json(spot_request=spot_request)


def main():

argument_spec = dict(
filters=dict(default={}, type='dict'),
spot_instance_request_ids=dict(default=[], type='list', elements='str'),
)
module = AnsibleAWSModule(
argument_spec=argument_spec,
supports_check_mode=True
)
try:
connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Failed to connect to AWS')

describe_spot_instance_requests(connection, module)


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions tests/integration/targets/ec2_spot_instance/aliases
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
cloud/aws
ec2_spot_instance_info
62 changes: 59 additions & 3 deletions tests/integration/targets/ec2_spot_instance/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
key_name: "{{ resource_prefix }}-keypair"
instance_type: "t2.medium"
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
tags:
ansible-test: "{{ resource_prefix }}"
register: create_result

- name: Assert that result has changed and request has been created
Expand All @@ -90,6 +92,17 @@
- create_result.spot_request.spot_instance_request_id is defined
- create_result.spot_request.launch_specification.subnet_id == vpc_subnet_result.subnet.id

- name: Get info about the spot instance request created
ec2_spot_instance_info:
spot_instance_request_ids:
- "{{ create_result.spot_request.spot_instance_request_id }}"
register: spot_instance_info_result

- name: Assert that the spot request created is open or active
assert:
that:
- spot_instance_info_result.spot_request[0].state in ['open', 'active']

- name: Create spot request with more complex options
ec2_spot_instance:
launch_specification:
Expand All @@ -104,6 +117,7 @@
volume_size: 5
network_interfaces:
- associate_public_ip_address: False
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
delete_on_termination: True
device_index: 0
placement:
Expand All @@ -117,6 +131,7 @@
snake_case: "hello_world"
"Title Case": "Hello World"
"lowercase spaced": "hello world"
ansible-test: "{{ resource_prefix }}"
register: complex_create_result

- assert:
Expand All @@ -135,7 +150,7 @@
- launch_spec.network_interfaces.0.device_index == 0
- launch_spec.network_interfaces.0.associate_public_ip_address == false
- launch_spec.network_interfaces.0.delete_on_termination == true
- spot_request_tags|length == 5
- spot_request_tags|length == 6
- spot_request_tags['camelCase'] == 'helloWorld'
- spot_request_tags['PascalCase'] == 'HelloWorld'
- spot_request_tags['snake_case'] == 'hello_world'
Expand All @@ -145,6 +160,39 @@
launch_spec: '{{ complex_create_result.spot_request.launch_specification }}'
spot_request_tags: '{{ complex_create_result.spot_request.tags }}'

- name: Get info about the complex spot instance request created
ec2_spot_instance_info:
spot_instance_request_ids:
- "{{ complex_create_result.spot_request.spot_instance_request_id }}"
register: complex_info_result

- name: Assert that the complex spot request created is open/active and correct keys are set
assert:
that:
- complex_info_result.spot_request[0].state in ['open', 'active']
- complex_create_result.spot_request.spot_price == complex_info_result.spot_request[0].spot_price
- create_launch_spec.block_device_mappings[0].ebs.volume_size == info_launch_spec.block_device_mappings[0].ebs.volume_size
- create_launch_spec.block_device_mappings[0].ebs.volume_type == info_launch_spec.block_device_mappings[0].ebs.volume_type
- create_launch_spec.network_interfaces[0].delete_on_termination == info_launch_spec.network_interfaces[0].delete_on_termination
vars:
create_launch_spec: "{{ complex_create_result.spot_request.launch_specification }}"
info_launch_spec: "{{ complex_info_result.spot_request[0].launch_specification }}"

- name: Get info about the created spot instance requests and filter result based on provided filters
ec2_spot_instance_info:
spot_instance_request_ids:
- '{{ create_result.spot_request.spot_instance_request_id }}'
- '{{ complex_create_result.spot_request.spot_instance_request_id }}'
filters:
tag:ansible-test: "{{ resource_prefix }}"
launch.block-device-mapping.device-name: /dev/sdb
register: spot_instance_info_filter_result

- name: Assert that the correct spot request was returned in the filtered result
assert:
that:
- spot_instance_info_filter_result.spot_request[0].spot_instance_request_id == complex_create_result.spot_request.spot_instance_request_id

# Assert check mode
- name: Create spot instance request (check_mode)
ec2_spot_instance:
Expand All @@ -153,6 +201,8 @@
key_name: "{{ resource_prefix }}-keypair"
instance_type: "t2.medium"
subnet_id: "{{ vpc_subnet_result.subnet.id }}"
tags:
ansible-test: "{{ resource_prefix }}"
check_mode: True
register: check_create_result

Expand Down Expand Up @@ -221,14 +271,20 @@
filters:
vpc-id: "{{ vpc_result.vpc.id }}"

- name: get all spot requests created during test
ec2_spot_instance_info:
filters:
tag:ansible-test: "{{ resource_prefix }}"
register: spot_request_list

- name: remove spot instance requests
ec2_spot_instance:
spot_instance_request_ids:
- '{{ create_result.spot_request.spot_instance_request_id }}'
- '{{ complex_create_result.spot_request.spot_instance_request_id }}'
- '{{ item.spot_instance_request_id }}'
state: 'absent'
ignore_errors: true
retries: 5
with_items: "{{ spot_request_list.spot_request }}"

- name: remove the security group
ec2_group:
Expand Down

0 comments on commit ccb1760

Please sign in to comment.