mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
parent
16bbc19674
commit
310eb833a4
3 changed files with 303 additions and 225 deletions
|
@ -25,46 +25,33 @@ module: ec2_ami
|
|||
version_added: "1.3"
|
||||
short_description: create or destroy an image in ec2
|
||||
description:
|
||||
- Creates or deletes ec2 images.
|
||||
- Registers or deregisters ec2 images.
|
||||
options:
|
||||
instance_id:
|
||||
description:
|
||||
- Instance ID to create the AMI from.
|
||||
required: false
|
||||
default: null
|
||||
name:
|
||||
description:
|
||||
- The name of the new AMI.
|
||||
required: false
|
||||
default: null
|
||||
architecture:
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- The target architecture of the image to register
|
||||
required: false
|
||||
default: x86_64
|
||||
kernel_id:
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- The target kernel id of the image to register
|
||||
required: false
|
||||
default: null
|
||||
- The target kernel id of the image to register.
|
||||
virtualization_type:
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- The virtualization type of the image to register
|
||||
required: false
|
||||
default: hvm
|
||||
- The virtualization type of the image to register.
|
||||
root_device_name:
|
||||
version_added: "2.3"
|
||||
description:
|
||||
- The root device name of the image to register
|
||||
required: false
|
||||
default: null
|
||||
- The root device name of the image to register.
|
||||
wait:
|
||||
description:
|
||||
- Wait for the AMI to be in state 'available' before returning.
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
wait_timeout:
|
||||
|
@ -73,61 +60,67 @@ options:
|
|||
default: 900
|
||||
state:
|
||||
description:
|
||||
- Create or deregister/delete AMI.
|
||||
required: false
|
||||
- Register or deregister an AMI.
|
||||
default: 'present'
|
||||
choices: [ "absent", "present" ]
|
||||
description:
|
||||
description:
|
||||
- Human-readable string describing the contents and purpose of the AMI.
|
||||
required: false
|
||||
default: null
|
||||
no_reboot:
|
||||
description:
|
||||
- Flag indicating that the bundling process should not attempt to shutdown the instance before bundling. If this flag is True, the
|
||||
responsibility of maintaining file system integrity is left to the owner of the instance.
|
||||
required: false
|
||||
default: no
|
||||
choices: [ "yes", "no" ]
|
||||
image_id:
|
||||
description:
|
||||
- Image ID to be deregistered.
|
||||
required: false
|
||||
default: null
|
||||
device_mapping:
|
||||
version_added: "2.0"
|
||||
description:
|
||||
- List of device hashes/dictionaries with custom configurations (same block-device-mapping parameters)
|
||||
- List of device hashes/dictionaries with custom configurations (same block-device-mapping parameters).
|
||||
- >
|
||||
Valid properties include: device_name, volume_type, size (in GB), delete_on_termination (boolean), no_device (boolean),
|
||||
snapshot_id, iops (for io1 volume_type)
|
||||
required: false
|
||||
default: null
|
||||
Valid properties include: device_name, volume_type, size/volume_size (in GB), delete_on_termination (boolean), no_device (boolean),
|
||||
snapshot_id, iops (for io1 volume_type), encrypted
|
||||
delete_snapshot:
|
||||
description:
|
||||
- Delete snapshots when deregistering the AMI.
|
||||
required: false
|
||||
default: "no"
|
||||
choices: [ "yes", "no" ]
|
||||
tags:
|
||||
description:
|
||||
- A dictionary of tags to add to the new image; '{"key":"value"}' and '{"key":"value","key":"value"}'
|
||||
required: false
|
||||
default: null
|
||||
version_added: "2.0"
|
||||
launch_permissions:
|
||||
description:
|
||||
- Users and groups that should be able to launch the AMI. Expects
|
||||
dictionary with a key of user_ids and/or group_names. user_ids should
|
||||
be a list of account ids. group_name should be a list of groups, "all"
|
||||
is the only acceptable value currently.
|
||||
required: false
|
||||
default: null
|
||||
- Users and groups that should be able to launch the AMI. Expects dictionary with a key of user_ids and/or group_names. user_ids should
|
||||
be a list of account ids. group_name should be a list of groups, "all" is the only acceptable value currently.
|
||||
version_added: "2.0"
|
||||
image_location:
|
||||
description:
|
||||
- The s3 location of an image to use for the AMI.
|
||||
version_added: "2.5"
|
||||
enhanced_networking:
|
||||
description:
|
||||
- A boolean representing whether enhanced networking with ENA is enabled or not.
|
||||
version_added: "2.5"
|
||||
billing_products:
|
||||
description:
|
||||
- A list of valid billing codes. To be used with valid accounts by aws marketplace vendors.
|
||||
version_added: "2.5"
|
||||
ramdisk_id:
|
||||
description:
|
||||
- The ID of the RAM disk.
|
||||
version_added: "2.5"
|
||||
sriov_net_support:
|
||||
description:
|
||||
- Set to simple to enable enhanced networking with the Intel 82599 Virtual Function interface for the AMI and any instances that you launch from the AMI.
|
||||
version_added: "2.5"
|
||||
author:
|
||||
- "Evan Duffield (@scicoin-project) <eduffield@iacquire.com>"
|
||||
- "Constantin Bugneac (@Constantin07) <constantin.bugneac@endava.com>"
|
||||
- "Ross Williams (@gunzy83) <gunzy83au@gmail.com>"
|
||||
- "Willem van Ketwich (@wilvk) <willvk@gmail.com>"
|
||||
extends_documentation_fragment:
|
||||
- aws
|
||||
- ec2
|
||||
|
@ -162,7 +155,7 @@ EXAMPLES = '''
|
|||
root_device_name: /dev/xvda
|
||||
device_mapping:
|
||||
- device_name: /dev/xvda
|
||||
size: 8
|
||||
volume_size: 8
|
||||
snapshot_id: snap-xxxxxxxx
|
||||
delete_on_termination: true
|
||||
volume_type: gp2
|
||||
|
@ -324,74 +317,72 @@ snapshots_deleted:
|
|||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.ec2 import ec2_connect, ec2_argument_spec
|
||||
from ansible.module_utils.ec2 import ec2_connect, ec2_argument_spec, ansible_dict_to_boto3_tag_list
|
||||
|
||||
import time
|
||||
import traceback
|
||||
from ansible.module_utils.ec2 import get_aws_connection_info, ec2_argument_spec, ec2_connect, boto3_conn, camel_dict_to_snake_dict, HAS_BOTO3
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
try:
|
||||
import boto
|
||||
import boto.ec2
|
||||
from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
|
||||
HAS_BOTO = True
|
||||
import botocore
|
||||
except ImportError:
|
||||
HAS_BOTO = False
|
||||
pass
|
||||
|
||||
|
||||
def get_block_device_mapping(image):
|
||||
"""
|
||||
Retrieves block device mapping from AMI
|
||||
"""
|
||||
|
||||
bdm_dict = dict()
|
||||
|
||||
if image is not None and hasattr(image, 'block_device_mapping'):
|
||||
bdm = getattr(image, 'block_device_mapping')
|
||||
for device_name in bdm.keys():
|
||||
bdm_dict[device_name] = {
|
||||
'size': bdm[device_name].size,
|
||||
'snapshot_id': bdm[device_name].snapshot_id,
|
||||
'volume_type': bdm[device_name].volume_type,
|
||||
'encrypted': bdm[device_name].encrypted,
|
||||
'delete_on_termination': bdm[device_name].delete_on_termination
|
||||
if image is not None and image.get('block_device_mappings') is not None:
|
||||
bdm = image.get('block_device_mappings')
|
||||
for device in bdm:
|
||||
device_name = device.get('device_name')
|
||||
ebs = device.get("ebs")
|
||||
bdm_dict_item = {
|
||||
'size': ebs.get("volume_size"),
|
||||
'snapshot_id': ebs.get("snapshot_id"),
|
||||
'volume_type': ebs.get("volume_type"),
|
||||
'encrypted': ebs.get("encrypted"),
|
||||
'delete_on_termination': ebs.get("delete_on_termination")
|
||||
}
|
||||
|
||||
bdm_dict[device_name] = bdm_dict_item
|
||||
return bdm_dict
|
||||
|
||||
|
||||
def get_ami_info(image):
|
||||
|
||||
def get_ami_info(camel_image):
|
||||
image = camel_dict_to_snake_dict(camel_image)
|
||||
return dict(
|
||||
image_id=image.id,
|
||||
state=image.state,
|
||||
architecture=image.architecture,
|
||||
image_id=image.get("image_id"),
|
||||
state=image.get("state"),
|
||||
architecture=image.get("architecture"),
|
||||
block_device_mapping=get_block_device_mapping(image),
|
||||
creationDate=image.creationDate,
|
||||
description=image.description,
|
||||
hypervisor=image.hypervisor,
|
||||
is_public=image.is_public,
|
||||
location=image.location,
|
||||
ownerId=image.ownerId,
|
||||
root_device_name=image.root_device_name,
|
||||
root_device_type=image.root_device_type,
|
||||
tags=image.tags,
|
||||
virtualization_type=image.virtualization_type,
|
||||
name=image.name,
|
||||
platform=image.platform
|
||||
creationDate=image.get("creation_date"),
|
||||
description=image.get("description"),
|
||||
hypervisor=image.get("hypervisor"),
|
||||
is_public=image.get("public"),
|
||||
location=image.get("image_location"),
|
||||
ownerId=image.get("owner_id"),
|
||||
root_device_name=image.get("root_device_name"),
|
||||
root_device_type=image.get("root_device_type"),
|
||||
tags=image.get("tags"),
|
||||
virtualization_type=image.get("virtualization_type"),
|
||||
name=image.get("name"),
|
||||
platform=image.get("platform"),
|
||||
enhanced_networking=image.get("ena_support"),
|
||||
image_owner_alias=image.get("image_owner_alias"),
|
||||
image_type=image.get("image_type"),
|
||||
kernel_id=image.get("kernel_id"),
|
||||
product_codes=image.get("product_codes"),
|
||||
ramdisk_id=image.get("ramdisk_id"),
|
||||
sriov_net_support=image.get("sriov_net_support"),
|
||||
state_reason=image.get("state_reason")
|
||||
)
|
||||
|
||||
|
||||
def create_image(module, ec2):
|
||||
"""
|
||||
Creates new AMI
|
||||
|
||||
module : AnsibleModule object
|
||||
ec2: authenticated ec2 connection object
|
||||
"""
|
||||
|
||||
def create_image(module, connection, resource):
|
||||
instance_id = module.params.get('instance_id')
|
||||
name = module.params.get('name')
|
||||
wait = module.params.get('wait')
|
||||
wait_timeout = int(module.params.get('wait_timeout'))
|
||||
wait_timeout = module.params.get('wait_timeout')
|
||||
description = module.params.get('description')
|
||||
architecture = module.params.get('architecture')
|
||||
kernel_id = module.params.get('kernel_id')
|
||||
|
@ -401,193 +392,264 @@ def create_image(module, ec2):
|
|||
device_mapping = module.params.get('device_mapping')
|
||||
tags = module.params.get('tags')
|
||||
launch_permissions = module.params.get('launch_permissions')
|
||||
image_location = module.params.get('image_location')
|
||||
enhanced_networking = module.params.get('enhanced_networking')
|
||||
billing_products = module.params.get('billing_products')
|
||||
ramdisk_id = module.params.get('ramdisk_id')
|
||||
sriov_net_support = module.params.get('sriov_net_support')
|
||||
|
||||
try:
|
||||
params = {'name': name,
|
||||
'description': description}
|
||||
params = {
|
||||
'Name': name,
|
||||
'Description': description
|
||||
}
|
||||
|
||||
images = ec2.get_all_images(filters={'name': name})
|
||||
images = connection.describe_images(
|
||||
Filters=[
|
||||
{
|
||||
'Name': 'name',
|
||||
'Values': [name]
|
||||
}
|
||||
]
|
||||
).get('Images')
|
||||
|
||||
# ensure that launch_permissions are up to date
|
||||
if images and images[0]:
|
||||
# ensure that launch_permissions are up to date
|
||||
update_image(module, ec2, images[0].id)
|
||||
update_image(module, connection, images[0].get('ImageId'), resource)
|
||||
|
||||
block_device_mapping = None
|
||||
|
||||
bdm = None
|
||||
if device_mapping:
|
||||
bdm = BlockDeviceMapping()
|
||||
block_device_mapping = []
|
||||
for device in device_mapping:
|
||||
device['Ebs'] = {}
|
||||
if 'device_name' not in device:
|
||||
module.fail_json(msg='Device name must be set for volume')
|
||||
device_name = device['device_name']
|
||||
del device['device_name']
|
||||
bd = BlockDeviceType(**device)
|
||||
bdm[device_name] = bd
|
||||
|
||||
module.fail_json(msg="Error - Device name must be set for volume.")
|
||||
device = rename_item_if_exists(device, 'device_name', 'DeviceName')
|
||||
device = rename_item_if_exists(device, 'virtual_name', 'VirtualName')
|
||||
device = rename_item_if_exists(device, 'no_device', 'NoDevice')
|
||||
device = rename_item_if_exists(device, 'volume_type', 'VolumeType', 'Ebs')
|
||||
device = rename_item_if_exists(device, 'snapshot_id', 'SnapshotId', 'Ebs')
|
||||
device = rename_item_if_exists(device, 'delete_on_termination', 'DeleteOnTermination', 'Ebs')
|
||||
device = rename_item_if_exists(device, 'size', 'VolumeSize', 'Ebs')
|
||||
device = rename_item_if_exists(device, 'volume_size', 'VolumeSize', 'Ebs')
|
||||
device = rename_item_if_exists(device, 'iops', 'Iops', 'Ebs')
|
||||
device = rename_item_if_exists(device, 'encrypted', 'Encrypted', 'Ebs')
|
||||
block_device_mapping.append(device)
|
||||
if block_device_mapping:
|
||||
params['BlockDeviceMappings'] = block_device_mapping
|
||||
if instance_id:
|
||||
params['instance_id'] = instance_id
|
||||
params['no_reboot'] = no_reboot
|
||||
if bdm:
|
||||
params['block_device_mapping'] = bdm
|
||||
image_id = ec2.create_image(**params)
|
||||
params['InstanceId'] = instance_id
|
||||
params['NoReboot'] = no_reboot
|
||||
image_id = connection.create_image(**params).get('ImageId')
|
||||
else:
|
||||
params['architecture'] = architecture
|
||||
params['virtualization_type'] = virtualization_type
|
||||
if architecture:
|
||||
params['Architecture'] = architecture
|
||||
if virtualization_type:
|
||||
params['VirtualizationType'] = virtualization_type
|
||||
if image_location:
|
||||
params['ImageLocation'] = image_location
|
||||
if enhanced_networking:
|
||||
params['EnaSupport'] = enhanced_networking
|
||||
if billing_products:
|
||||
params['BillingProducts'] = billing_products
|
||||
if ramdisk_id:
|
||||
params['RamdiskId'] = ramdisk_id
|
||||
if sriov_net_support:
|
||||
params['SriovNetSupport'] = sriov_net_support
|
||||
if kernel_id:
|
||||
params['kernel_id'] = kernel_id
|
||||
params['KernelId'] = kernel_id
|
||||
if root_device_name:
|
||||
params['root_device_name'] = root_device_name
|
||||
if bdm:
|
||||
params['block_device_map'] = bdm
|
||||
image_id = ec2.register_image(**params)
|
||||
except boto.exception.BotoServerError as e:
|
||||
module.fail_json(msg="%s: %s" % (e.error_code, e.error_message))
|
||||
params['RootDeviceName'] = root_device_name
|
||||
image_id = connection.register_image(**params).get('ImageId')
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json(msg="Error registering image - " + str(e), exception=traceback.format_exc(),
|
||||
**camel_dict_to_snake_dict(e.response))
|
||||
|
||||
# Wait until the image is recognized. EC2 API has eventual consistency,
|
||||
# such that a successful CreateImage API call doesn't guarantee the success
|
||||
# of subsequent DescribeImages API call using the new image id returned.
|
||||
for i in range(wait_timeout):
|
||||
try:
|
||||
img = ec2.get_image(image_id)
|
||||
|
||||
if img.state == 'available':
|
||||
image = get_image_by_id(module, connection, image_id)
|
||||
if image.get('State') == 'available':
|
||||
break
|
||||
elif img.state == 'failed':
|
||||
module.fail_json(msg="AMI creation failed, please see the AWS console for more details")
|
||||
except boto.exception.EC2ResponseError as e:
|
||||
elif image.get('State') == 'failed':
|
||||
module.fail_json(msg="AMI creation failed, please see the AWS console for more details.")
|
||||
except botocore.exceptions.ClientError as e:
|
||||
if ('InvalidAMIID.NotFound' not in e.error_code and 'InvalidAMIID.Unavailable' not in e.error_code) and wait and i == wait_timeout - 1:
|
||||
module.fail_json(msg="Error while trying to find the new image. Using wait=yes and/or a longer "
|
||||
"wait_timeout may help. %s: %s" % (e.error_code, e.error_message))
|
||||
module.fail_json(msg="Error while trying to find the new image. Using wait=yes and/or a longer wait_timeout may help. %s: %s"
|
||||
% (e.error_code, e.error_message))
|
||||
finally:
|
||||
time.sleep(1)
|
||||
|
||||
if img.state != 'available':
|
||||
module.fail_json(msg="Error while trying to find the new image. Using wait=yes and/or a longer wait_timeout may help.")
|
||||
|
||||
if tags:
|
||||
try:
|
||||
ec2.create_tags(image_id, tags)
|
||||
except boto.exception.EC2ResponseError as e:
|
||||
module.fail_json(msg="Image tagging failed => %s: %s" % (e.error_code, e.error_message))
|
||||
connection.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags))
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json(msg="Error tagging image - " + str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||
|
||||
if launch_permissions:
|
||||
try:
|
||||
img = ec2.get_image(image_id)
|
||||
img.set_launch_permissions(**launch_permissions)
|
||||
except boto.exception.BotoServerError as e:
|
||||
module.fail_json(msg="%s: %s" % (e.error_code, e.error_message), image_id=image_id)
|
||||
image = get_image_by_id(module, connection, image_id)
|
||||
image.set_launch_permissions(**launch_permissions)
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json(msg="Error setting launch permissions for image: " + image_id + " - " + str(e), exception=traceback.format_exc(),
|
||||
**camel_dict_to_snake_dict(e.response))
|
||||
|
||||
module.exit_json(msg="AMI creation operation complete", changed=True, **get_ami_info(img))
|
||||
module.exit_json(msg="AMI creation operation complete.", changed=True, **get_ami_info(image))
|
||||
|
||||
|
||||
def deregister_image(module, ec2):
|
||||
"""
|
||||
Deregisters AMI
|
||||
"""
|
||||
|
||||
def deregister_image(module, connection):
|
||||
image_id = module.params.get('image_id')
|
||||
delete_snapshot = module.params.get('delete_snapshot')
|
||||
wait = module.params.get('wait')
|
||||
wait_timeout = int(module.params.get('wait_timeout'))
|
||||
wait_timeout = module.params.get('wait_timeout')
|
||||
image = get_image_by_id(module, connection, image_id)
|
||||
|
||||
img = ec2.get_image(image_id)
|
||||
if img is None:
|
||||
module.fail_json(msg="Image %s does not exist" % image_id, changed=False)
|
||||
if image is None:
|
||||
module.fail_json(msg="Image %s does not exist." % image_id, changed=False)
|
||||
|
||||
# Get all associated snapshot ids before deregistering image otherwise this information becomes unavailable
|
||||
# Get all associated snapshot ids before deregistering image otherwise this information becomes unavailable.
|
||||
snapshots = []
|
||||
if hasattr(img, 'block_device_mapping'):
|
||||
for key in img.block_device_mapping:
|
||||
snapshots.append(img.block_device_mapping[key].snapshot_id)
|
||||
if 'BlockDeviceMappings' in image:
|
||||
for mapping in image.get('BlockDeviceMappings'):
|
||||
snapshot_id = mapping.get('SnapshotId')
|
||||
if snapshot_id is not None:
|
||||
snapshots.append(snapshot_id)
|
||||
|
||||
# When trying to re-delete already deleted image it doesn't raise an exception
|
||||
# It just returns an object without image attributes
|
||||
if hasattr(img, 'id'):
|
||||
# When trying to re-deregister an already deregistered image it doesn't raise an exception, it just returns an object without image attributes.
|
||||
if 'ImageId' in image:
|
||||
try:
|
||||
params = {'image_id': image_id,
|
||||
'delete_snapshot': delete_snapshot}
|
||||
ec2.deregister_image(**params)
|
||||
except boto.exception.BotoServerError as e:
|
||||
module.fail_json(msg="%s: %s" % (e.error_code, e.error_message))
|
||||
res = connection.deregister_image(ImageId=image_id)
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json(msg="Error deregistering image - " + str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||
else:
|
||||
module.exit_json(msg="Image %s has already been deleted" % image_id, changed=False)
|
||||
module.exit_json(msg="Image %s has already been deregistered." % image_id, changed=False)
|
||||
|
||||
# wait here until the image is gone
|
||||
img = ec2.get_image(image_id)
|
||||
image = get_image_by_id(module, connection, image_id)
|
||||
wait_timeout = time.time() + wait_timeout
|
||||
while wait and wait_timeout > time.time() and img is not None:
|
||||
img = ec2.get_image(image_id)
|
||||
time.sleep(3)
|
||||
if wait and wait_timeout <= time.time():
|
||||
# waiting took too long
|
||||
module.fail_json(msg="timed out waiting for image to be deregistered/deleted")
|
||||
|
||||
# Boto library has hardcoded the deletion of the snapshot for the root volume mounted as '/dev/sda1' only
|
||||
# Make it possible to delete all snapshots which belong to image, including root block device mapped as '/dev/xvda'
|
||||
while wait and wait_timeout > time.time() and image is not None:
|
||||
image = get_image_by_id(module, connection, image_id)
|
||||
time.sleep(3)
|
||||
|
||||
if wait and wait_timeout <= time.time():
|
||||
module.fail_json(msg="Timed out waiting for image to be deregistered.")
|
||||
|
||||
exit_params = {'msg': "AMI deregister operation complete.", 'changed': True}
|
||||
|
||||
if delete_snapshot:
|
||||
try:
|
||||
for snapshot_id in snapshots:
|
||||
ec2.delete_snapshot(snapshot_id)
|
||||
except boto.exception.BotoServerError as e:
|
||||
connection.delete_snapshot(SnapshotId=snapshot_id)
|
||||
except botocore.exceptions.ClientError as e:
|
||||
# Don't error out if root volume snapshot was already deregistered as part of deregister_image
|
||||
if e.error_code == 'InvalidSnapshot.NotFound':
|
||||
# Don't error out if root volume snapshot was already deleted as part of deregister_image
|
||||
pass
|
||||
module.exit_json(msg="AMI deregister/delete operation complete", changed=True, snapshots_deleted=snapshots)
|
||||
else:
|
||||
module.exit_json(msg="AMI deregister/delete operation complete", changed=True)
|
||||
exit_params['snapshots_deleted'] = snapshots
|
||||
|
||||
module.exit_json(**exit_params)
|
||||
|
||||
|
||||
def update_image(module, ec2, image_id):
|
||||
"""
|
||||
Updates AMI
|
||||
"""
|
||||
|
||||
def update_image(module, connection, image_id, resource):
|
||||
launch_permissions = module.params.get('launch_permissions') or []
|
||||
|
||||
if 'user_ids' in launch_permissions:
|
||||
launch_permissions['user_ids'] = [str(user_id) for user_id in launch_permissions['user_ids']]
|
||||
|
||||
img = ec2.get_image(image_id)
|
||||
if img is None:
|
||||
image = resource.Image(image_id)
|
||||
|
||||
if image is None:
|
||||
module.fail_json(msg="Image %s does not exist" % image_id, changed=False)
|
||||
|
||||
try:
|
||||
set_permissions = img.get_launch_permissions()
|
||||
set_permissions = connection.describe_image_attribute(Attribute='launchPermission', ImageId=image_id).get('LaunchPermissions')
|
||||
if set_permissions != launch_permissions:
|
||||
if (('user_ids' in launch_permissions and launch_permissions['user_ids']) or
|
||||
('group_names' in launch_permissions and launch_permissions['group_names'])):
|
||||
img.set_launch_permissions(**launch_permissions)
|
||||
elif ('user_ids' in set_permissions and set_permissions['user_ids']) or ('group_names' in set_permissions and set_permissions['group_names']):
|
||||
img.remove_launch_permissions(**set_permissions)
|
||||
if ('user_ids' in launch_permissions or 'group_names' in launch_permissions):
|
||||
group_names = launch_permissions.get('group_names')[0] if launch_permissions.get('group_names') else None
|
||||
user_ids = launch_permissions.get('user_ids')[0] if launch_permissions.get('user_ids') else None
|
||||
launch_perms_add = {'Add': [{}]}
|
||||
if group_names:
|
||||
launch_perms_add['Add'][0]['Group'] = group_names
|
||||
if user_ids:
|
||||
launch_perms_add['Add'][0]['UserId'] = user_ids
|
||||
image.modify_attribute(Attribute='launchPermission', LaunchPermission=launch_perms_add)
|
||||
elif set_permissions and set_permissions[0].get('UserId') is not None and set_permissions[0].get('Group') is not None:
|
||||
image.modify_attribute(
|
||||
Attribute='launchPermission',
|
||||
LaunchPermission={
|
||||
'Remove': [{
|
||||
'Group': (set_permissions.get('Group') or ''),
|
||||
'UserId': (set_permissions.get('UserId') or '')
|
||||
}]
|
||||
})
|
||||
else:
|
||||
module.exit_json(msg="AMI not updated", launch_permissions=set_permissions, changed=False, **get_ami_info(img))
|
||||
module.exit_json(msg="AMI launch permissions updated", launch_permissions=launch_permissions,
|
||||
set_perms=set_permissions, changed=True, **get_ami_info(img))
|
||||
module.exit_json(msg="AMI not updated.", launch_permissions=set_permissions, changed=False,
|
||||
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
||||
module.exit_json(msg="AMI launch permissions updated.", launch_permissions=launch_permissions, set_perms=set_permissions, changed=True,
|
||||
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
||||
else:
|
||||
module.exit_json(msg="AMI not updated", launch_permissions=set_permissions, changed=False, **get_ami_info(img))
|
||||
module.exit_json(msg="AMI not updated.", launch_permissions=set_permissions, changed=False,
|
||||
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json(msg="Error updating image - " + str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||
|
||||
except boto.exception.BotoServerError as e:
|
||||
module.fail_json(msg="%s: %s" % (e.error_code, e.error_message))
|
||||
|
||||
def get_image_by_id(module, connection, image_id):
|
||||
try:
|
||||
images_response = connection.describe_images(ImageIds=[image_id])
|
||||
images = images_response.get('Images')
|
||||
no_images = len(images)
|
||||
if no_images == 0:
|
||||
return None
|
||||
if no_images == 1:
|
||||
return images[0]
|
||||
module.fail_json(msg="Invalid number of instances (%s) found for image_id: %s." % (str(len(images)), image_id))
|
||||
except botocore.exceptions.ClientError as e:
|
||||
module.fail_json(msg="Error retreiving image by image_id - " + str(e), exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
|
||||
|
||||
|
||||
def rename_item_if_exists(dict_object, attribute, new_attribute, child_node=None):
|
||||
new_item = dict_object.get(attribute)
|
||||
if new_item is not None:
|
||||
if child_node is None:
|
||||
dict_object[new_attribute] = dict_object.get(attribute)
|
||||
else:
|
||||
dict_object[child_node][new_attribute] = dict_object.get(attribute)
|
||||
dict_object.pop(attribute)
|
||||
return dict_object
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = ec2_argument_spec()
|
||||
argument_spec.update(
|
||||
dict(
|
||||
instance_id=dict(),
|
||||
image_id=dict(),
|
||||
architecture=dict(default="x86_64"),
|
||||
kernel_id=dict(),
|
||||
virtualization_type=dict(default="hvm"),
|
||||
root_device_name=dict(),
|
||||
delete_snapshot=dict(default=False, type='bool'),
|
||||
name=dict(),
|
||||
wait=dict(type='bool', default=False),
|
||||
wait_timeout=dict(default=900),
|
||||
description=dict(default=""),
|
||||
no_reboot=dict(default=False, type='bool'),
|
||||
state=dict(default='present'),
|
||||
device_mapping=dict(type='list'),
|
||||
tags=dict(type='dict'),
|
||||
launch_permissions=dict(type='dict')
|
||||
)
|
||||
argument_spec.update(dict(
|
||||
instance_id=dict(),
|
||||
image_id=dict(),
|
||||
architecture=dict(default='x86_64'),
|
||||
kernel_id=dict(),
|
||||
virtualization_type=dict(default='hvm'),
|
||||
root_device_name=dict(),
|
||||
delete_snapshot=dict(default=False, type='bool'),
|
||||
name=dict(),
|
||||
wait=dict(type='bool', default=False),
|
||||
wait_timeout=dict(default=900, type='int'),
|
||||
description=dict(default=''),
|
||||
no_reboot=dict(default=False, type='bool'),
|
||||
state=dict(default='present'),
|
||||
device_mapping=dict(type='list'),
|
||||
tags=dict(type='dict'),
|
||||
launch_permissions=dict(type='dict'),
|
||||
image_location=dict(),
|
||||
enhanced_networking=dict(type='bool'),
|
||||
billing_products=dict(type='list'),
|
||||
ramdisk_id=dict(),
|
||||
sriov_net_support=dict()
|
||||
))
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_if=[
|
||||
['state', 'absent', ['image_id']],
|
||||
['state', 'present', ['name']],
|
||||
]
|
||||
)
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
|
@ -595,26 +657,24 @@ def main():
|
|||
('state', 'absent', ('image_id',))]
|
||||
)
|
||||
|
||||
if not HAS_BOTO:
|
||||
module.fail_json(msg='boto required for this module')
|
||||
if not HAS_BOTO3:
|
||||
module.fail_json(msg='boto3 required for this module')
|
||||
|
||||
try:
|
||||
ec2 = ec2_connect(module)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Error while connecting to aws: %s" % str(e))
|
||||
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
|
||||
connection = boto3_conn(module, conn_type='client', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs)
|
||||
resource = boto3_conn(module, conn_type='resource', resource='ec2', region=region, endpoint=ec2_url, **aws_connect_kwargs)
|
||||
except botocore.exceptions.NoRegionError:
|
||||
module.fail_json(msg=("Region must be specified as a parameter in AWS_DEFAULT_REGION environment variable or in boto configuration file."))
|
||||
|
||||
if module.params.get('state') == 'absent':
|
||||
deregister_image(module, ec2)
|
||||
|
||||
deregister_image(module, connection)
|
||||
elif module.params.get('state') == 'present':
|
||||
if module.params.get('image_id') and module.params.get('launch_permissions'):
|
||||
# Update image's launch permissions
|
||||
update_image(module, ec2, module.params.get('image_id'))
|
||||
|
||||
# Changed is always set to true when provisioning new AMI
|
||||
update_image(module, connection, module.params.get('image_id'), resource)
|
||||
if not module.params.get('instance_id') and not module.params.get('device_mapping'):
|
||||
module.fail_json(msg='instance_id or device_mapping (register from ebs snapshot) parameter is required for new image')
|
||||
create_image(module, ec2)
|
||||
module.fail_json(msg="The parameters instance_id or device_mapping (register from EBS snapshot) are required for a new image.")
|
||||
create_image(module, connection, resource)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
key_name: '{{ setup_key.key.name }}'
|
||||
instance_type: t2.micro
|
||||
state: present
|
||||
image: '{{ ec2_ami_image[ec2_region] }}'
|
||||
image: '{{ ec2_region_images[ec2_region] }}'
|
||||
wait: yes
|
||||
instance_tags:
|
||||
'{{ec2_ami_name}}_instance_setup': 'integration_tests'
|
||||
|
|
|
@ -1,2 +1,20 @@
|
|||
---
|
||||
# vars file for test_ec2_ami
|
||||
|
||||
# based on Amazon Linux AMI 2017.09.0 (HVM), SSD Volume Type
|
||||
ec2_region_images:
|
||||
us-east-1: ami-8c1be5f6
|
||||
us-east-2: ami-c5062ba0
|
||||
us-west-1: ami-02eada62
|
||||
us-west-2: ami-e689729e
|
||||
ca-central-1: ami-fd55ec99
|
||||
eu-west-1: ami-acd005d5
|
||||
eu-central-1: ami-c7ee5ca8
|
||||
eu-west-2: ami-1a7f6d7e
|
||||
ap-southeast-1: ami-0797ea64
|
||||
ap-southeast-2: ami-8536d6e7
|
||||
ap-northeast-2: ami-9bec36f5
|
||||
ap-northeast-1: ami-2a69be4c
|
||||
ap-south-1: ami-4fc58420
|
||||
sa-east-1: ami-f1344b9d
|
||||
cn-north-1: ami-fba67596
|
||||
|
|
Loading…
Reference in a new issue