[cloud] Follow up on FIXMEs in ec2_ami & ec2_ami tests (#32337)
* Several tests were marked as FIXME and should have been fixed with the boto3 move. * Improved tags output. Add purge_tags option (default: no) * Allow description and tags update * Return launch_permissions * Allow empty launch permissions for image creation * Empty launch permissions should work the same way for image creation as no launch permissions * Cope with ephemeral devices in AMI block device mapping * Ephemeral devices can appear in AMI block devices, and this information should be returned * Fix notation for creating sets from comprehensions
This commit is contained in:
parent
1eae3b6b59
commit
60b29cf57d
2 changed files with 223 additions and 192 deletions
|
@ -1,18 +1,9 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# This file is part of Ansible
|
# Copyright: Ansible Project
|
||||||
#
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
# Ansible is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
from __future__ import absolute_import, division, print_function
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
__metaclass__ = type
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Ansible is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||||
'status': ['stableinterface'],
|
'status': ['stableinterface'],
|
||||||
|
@ -91,10 +82,15 @@ options:
|
||||||
description:
|
description:
|
||||||
- A dictionary of tags to add to the new image; '{"key":"value"}' and '{"key":"value","key":"value"}'
|
- A dictionary of tags to add to the new image; '{"key":"value"}' and '{"key":"value","key":"value"}'
|
||||||
version_added: "2.0"
|
version_added: "2.0"
|
||||||
|
purge_tags:
|
||||||
|
description: Whether to remove existing tags that aren't passed in the C(tags) parameter
|
||||||
|
version_added: "2.5"
|
||||||
|
default: "no"
|
||||||
launch_permissions:
|
launch_permissions:
|
||||||
description:
|
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
|
- 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.
|
be a list of account ids. group_name should be a list of groups, "all" is the only acceptable value currently.
|
||||||
|
- You must pass all desired launch permissions if you wish to modify existing launch permissions (passing just groups will remove all users)
|
||||||
version_added: "2.0"
|
version_added: "2.0"
|
||||||
image_location:
|
image_location:
|
||||||
description:
|
description:
|
||||||
|
@ -257,6 +253,12 @@ is_public:
|
||||||
returned: when AMI is created or already exists
|
returned: when AMI is created or already exists
|
||||||
type: bool
|
type: bool
|
||||||
sample: false
|
sample: false
|
||||||
|
launch_permission:
|
||||||
|
description: permissions allowing other accounts to access the AMI
|
||||||
|
returned: when AMI is created or already exists
|
||||||
|
type: list
|
||||||
|
sample:
|
||||||
|
- group: "all"
|
||||||
location:
|
location:
|
||||||
description: location of image
|
description: location of image
|
||||||
returned: when AMI is created or already exists
|
returned: when AMI is created or already exists
|
||||||
|
@ -315,14 +317,10 @@ snapshots_deleted:
|
||||||
]
|
]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# import module snippets
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
|
||||||
from ansible.module_utils.ec2 import ec2_connect, ec2_argument_spec, ansible_dict_to_boto3_tag_list
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import traceback
|
from ansible.module_utils.ec2 import get_aws_connection_info, ec2_argument_spec, boto3_conn, camel_dict_to_snake_dict
|
||||||
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.ec2 import ansible_dict_to_boto3_tag_list, boto3_tag_list_to_ansible_dict, compare_aws_tags
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import botocore
|
import botocore
|
||||||
|
@ -336,6 +334,7 @@ def get_block_device_mapping(image):
|
||||||
bdm = image.get('block_device_mappings')
|
bdm = image.get('block_device_mappings')
|
||||||
for device in bdm:
|
for device in bdm:
|
||||||
device_name = device.get('device_name')
|
device_name = device.get('device_name')
|
||||||
|
if 'ebs' in device:
|
||||||
ebs = device.get("ebs")
|
ebs = device.get("ebs")
|
||||||
bdm_dict_item = {
|
bdm_dict_item = {
|
||||||
'size': ebs.get("volume_size"),
|
'size': ebs.get("volume_size"),
|
||||||
|
@ -344,6 +343,8 @@ def get_block_device_mapping(image):
|
||||||
'encrypted': ebs.get("encrypted"),
|
'encrypted': ebs.get("encrypted"),
|
||||||
'delete_on_termination': ebs.get("delete_on_termination")
|
'delete_on_termination': ebs.get("delete_on_termination")
|
||||||
}
|
}
|
||||||
|
elif 'virtual_name' in device:
|
||||||
|
bdm_dict_item = dict(virtual_name=device['virtual_name'])
|
||||||
bdm_dict[device_name] = bdm_dict_item
|
bdm_dict[device_name] = bdm_dict_item
|
||||||
return bdm_dict
|
return bdm_dict
|
||||||
|
|
||||||
|
@ -363,9 +364,9 @@ def get_ami_info(camel_image):
|
||||||
ownerId=image.get("owner_id"),
|
ownerId=image.get("owner_id"),
|
||||||
root_device_name=image.get("root_device_name"),
|
root_device_name=image.get("root_device_name"),
|
||||||
root_device_type=image.get("root_device_type"),
|
root_device_type=image.get("root_device_type"),
|
||||||
tags=image.get("tags"),
|
|
||||||
virtualization_type=image.get("virtualization_type"),
|
virtualization_type=image.get("virtualization_type"),
|
||||||
name=image.get("name"),
|
name=image.get("name"),
|
||||||
|
tags=boto3_tag_list_to_ansible_dict(image.get('tags')),
|
||||||
platform=image.get("platform"),
|
platform=image.get("platform"),
|
||||||
enhanced_networking=image.get("ena_support"),
|
enhanced_networking=image.get("ena_support"),
|
||||||
image_owner_alias=image.get("image_owner_alias"),
|
image_owner_alias=image.get("image_owner_alias"),
|
||||||
|
@ -374,11 +375,12 @@ def get_ami_info(camel_image):
|
||||||
product_codes=image.get("product_codes"),
|
product_codes=image.get("product_codes"),
|
||||||
ramdisk_id=image.get("ramdisk_id"),
|
ramdisk_id=image.get("ramdisk_id"),
|
||||||
sriov_net_support=image.get("sriov_net_support"),
|
sriov_net_support=image.get("sriov_net_support"),
|
||||||
state_reason=image.get("state_reason")
|
state_reason=image.get("state_reason"),
|
||||||
|
launch_permissions=image.get('launch_permissions')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_image(module, connection, resource):
|
def create_image(module, connection):
|
||||||
instance_id = module.params.get('instance_id')
|
instance_id = module.params.get('instance_id')
|
||||||
name = module.params.get('name')
|
name = module.params.get('name')
|
||||||
wait = module.params.get('wait')
|
wait = module.params.get('wait')
|
||||||
|
@ -413,10 +415,6 @@ def create_image(module, connection, resource):
|
||||||
]
|
]
|
||||||
).get('Images')
|
).get('Images')
|
||||||
|
|
||||||
# ensure that launch_permissions are up to date
|
|
||||||
if images and images[0]:
|
|
||||||
update_image(module, connection, images[0].get('ImageId'), resource)
|
|
||||||
|
|
||||||
block_device_mapping = None
|
block_device_mapping = None
|
||||||
|
|
||||||
if device_mapping:
|
if device_mapping:
|
||||||
|
@ -462,39 +460,35 @@ def create_image(module, connection, resource):
|
||||||
if root_device_name:
|
if root_device_name:
|
||||||
params['RootDeviceName'] = root_device_name
|
params['RootDeviceName'] = root_device_name
|
||||||
image_id = connection.register_image(**params).get('ImageId')
|
image_id = connection.register_image(**params).get('ImageId')
|
||||||
except botocore.exceptions.ClientError as e:
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
module.fail_json(msg="Error registering image - " + str(e), exception=traceback.format_exc(),
|
module.fail_json_aws(e, msg="Error registering image")
|
||||||
**camel_dict_to_snake_dict(e.response))
|
|
||||||
|
|
||||||
for i in range(wait_timeout):
|
if wait:
|
||||||
try:
|
waiter = connection.get_waiter('image_available')
|
||||||
image = get_image_by_id(module, connection, image_id)
|
delay = wait_timeout // 30
|
||||||
if image.get('State') == 'available':
|
max_attempts = 30
|
||||||
break
|
waiter.wait(ImageIds=[image_id], WaiterConfig=dict(Delay=delay, MaxAttempts=max_attempts))
|
||||||
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))
|
|
||||||
finally:
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
if tags:
|
if tags:
|
||||||
try:
|
try:
|
||||||
connection.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags))
|
connection.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags))
|
||||||
except botocore.exceptions.ClientError as e:
|
except (botocore.exceptions.BotoCoreError, 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))
|
module.fail_json_aws(e, msg="Error tagging image")
|
||||||
|
|
||||||
if launch_permissions:
|
if launch_permissions:
|
||||||
try:
|
try:
|
||||||
image = get_image_by_id(module, connection, image_id)
|
params = dict(Attribute='LaunchPermission', ImageId=image_id, LaunchPermission=dict(Add=list()))
|
||||||
image.set_launch_permissions(**launch_permissions)
|
for group_name in launch_permissions.get('group_names', []):
|
||||||
except botocore.exceptions.ClientError as e:
|
params['LaunchPermission']['Add'].append(dict(Group=group_name))
|
||||||
module.fail_json(msg="Error setting launch permissions for image: " + image_id + " - " + str(e), exception=traceback.format_exc(),
|
for user_id in launch_permissions.get('user_ids', []):
|
||||||
**camel_dict_to_snake_dict(e.response))
|
params['LaunchPermission']['Add'].append(dict(UserId=str(user_id)))
|
||||||
|
if params['LaunchPermission']['Add']:
|
||||||
|
connection.modify_image_attribute(**params)
|
||||||
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
|
module.fail_json_aws(e, msg="Error setting launch permissions for image %s" % image_id)
|
||||||
|
|
||||||
module.exit_json(msg="AMI creation operation complete.", changed=True, **get_ami_info(image))
|
module.exit_json(msg="AMI creation operation complete.", changed=True,
|
||||||
|
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
||||||
|
|
||||||
|
|
||||||
def deregister_image(module, connection):
|
def deregister_image(module, connection):
|
||||||
|
@ -505,7 +499,7 @@ def deregister_image(module, connection):
|
||||||
image = get_image_by_id(module, connection, image_id)
|
image = get_image_by_id(module, connection, image_id)
|
||||||
|
|
||||||
if image is None:
|
if image is None:
|
||||||
module.fail_json(msg="Image %s does not exist." % image_id, changed=False)
|
module.exit_json(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 = []
|
snapshots = []
|
||||||
|
@ -518,9 +512,9 @@ def deregister_image(module, connection):
|
||||||
# When trying to re-deregister an already deregistered image it doesn't raise an exception, it just returns an object without image attributes.
|
# 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:
|
if 'ImageId' in image:
|
||||||
try:
|
try:
|
||||||
res = connection.deregister_image(ImageId=image_id)
|
connection.deregister_image(ImageId=image_id)
|
||||||
except botocore.exceptions.ClientError as e:
|
except (botocore.exceptions.BotoCoreError, 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))
|
module.fail_json_aws(e, msg="Error deregistering image")
|
||||||
else:
|
else:
|
||||||
module.exit_json(msg="Image %s has already been deregistered." % image_id, changed=False)
|
module.exit_json(msg="Image %s has already been deregistered." % image_id, changed=False)
|
||||||
|
|
||||||
|
@ -542,69 +536,103 @@ def deregister_image(module, connection):
|
||||||
connection.delete_snapshot(SnapshotId=snapshot_id)
|
connection.delete_snapshot(SnapshotId=snapshot_id)
|
||||||
except botocore.exceptions.ClientError as e:
|
except botocore.exceptions.ClientError as e:
|
||||||
# Don't error out if root volume snapshot was already deregistered as part of deregister_image
|
# Don't error out if root volume snapshot was already deregistered as part of deregister_image
|
||||||
if e.error_code == 'InvalidSnapshot.NotFound':
|
if e.response['Error']['Code'] == 'InvalidSnapshot.NotFound':
|
||||||
pass
|
pass
|
||||||
exit_params['snapshots_deleted'] = snapshots
|
exit_params['snapshots_deleted'] = snapshots
|
||||||
|
|
||||||
module.exit_json(**exit_params)
|
module.exit_json(**exit_params)
|
||||||
|
|
||||||
|
|
||||||
def update_image(module, connection, image_id, resource):
|
def update_image(module, connection, image_id):
|
||||||
launch_permissions = module.params.get('launch_permissions') or []
|
launch_permissions = module.params.get('launch_permissions')
|
||||||
|
image = get_image_by_id(module, connection, image_id)
|
||||||
if 'user_ids' in launch_permissions:
|
|
||||||
launch_permissions['user_ids'] = [str(user_id) for user_id in launch_permissions['user_ids']]
|
|
||||||
|
|
||||||
image = resource.Image(image_id)
|
|
||||||
|
|
||||||
if image is None:
|
if image is None:
|
||||||
module.fail_json(msg="Image %s does not exist" % image_id, changed=False)
|
module.fail_json(msg="Image %s does not exist" % image_id, changed=False)
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
if launch_permissions is not None:
|
||||||
|
current_permissions = image['LaunchPermissions']
|
||||||
|
|
||||||
|
current_users = set(permission['UserId'] for permission in current_permissions if 'UserId' in permission)
|
||||||
|
desired_users = set(str(user_id) for user_id in launch_permissions.get('user_ids', []))
|
||||||
|
current_groups = set(permission['Group'] for permission in current_permissions if 'Group' in permission)
|
||||||
|
desired_groups = set(launch_permissions.get('group_names', []))
|
||||||
|
|
||||||
|
to_add_users = desired_users - current_users
|
||||||
|
to_remove_users = current_users - desired_users
|
||||||
|
to_add_groups = desired_groups - current_groups
|
||||||
|
to_remove_groups = current_groups - desired_groups
|
||||||
|
|
||||||
|
to_add = [dict(Group=group) for group in to_add_groups] + [dict(UserId=user_id) for user_id in to_add_users]
|
||||||
|
to_remove = [dict(Group=group) for group in to_remove_groups] + [dict(UserId=user_id) for user_id in to_remove_users]
|
||||||
|
|
||||||
|
if to_add or to_remove:
|
||||||
try:
|
try:
|
||||||
set_permissions = connection.describe_image_attribute(Attribute='launchPermission', ImageId=image_id).get('LaunchPermissions')
|
connection.modify_image_attribute(ImageId=image_id, Attribute='launchPermission',
|
||||||
if set_permissions != launch_permissions:
|
LaunchPermission=dict(Add=to_add, Remove=to_remove))
|
||||||
if ('user_ids' in launch_permissions or 'group_names' in launch_permissions):
|
changed = True
|
||||||
group_names = launch_permissions.get('group_names')[0] if launch_permissions.get('group_names') else None
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
user_ids = launch_permissions.get('user_ids')[0] if launch_permissions.get('user_ids') else None
|
module.fail_json_aws(e, msg="Error updating launch permissions")
|
||||||
launch_perms_add = {'Add': [{}]}
|
|
||||||
if group_names:
|
desired_tags = module.params.get('tags')
|
||||||
launch_perms_add['Add'][0]['Group'] = group_names
|
if desired_tags is not None:
|
||||||
if user_ids:
|
current_tags = boto3_tag_list_to_ansible_dict(image.get('Tags'))
|
||||||
launch_perms_add['Add'][0]['UserId'] = user_ids
|
tags_to_add, tags_to_remove = compare_aws_tags(current_tags, desired_tags, purge_tags=module.params.get('purge_tags'))
|
||||||
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:
|
if tags_to_remove:
|
||||||
image.modify_attribute(
|
try:
|
||||||
Attribute='launchPermission',
|
connection.delete_tags(Resources=[image_id], Tags=[dict(Key=tagkey) for tagkey in tags_to_remove])
|
||||||
LaunchPermission={
|
changed = True
|
||||||
'Remove': [{
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
'Group': (set_permissions.get('Group') or ''),
|
module.fail_json_aws(e, msg="Error updating tags")
|
||||||
'UserId': (set_permissions.get('UserId') or '')
|
|
||||||
}]
|
if tags_to_add:
|
||||||
})
|
try:
|
||||||
else:
|
connection.create_tags(Resources=[image_id], Tags=ansible_dict_to_boto3_tag_list(tags_to_add))
|
||||||
module.exit_json(msg="AMI not updated.", launch_permissions=set_permissions, changed=False,
|
changed = True
|
||||||
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
module.exit_json(msg="AMI launch permissions updated.", launch_permissions=launch_permissions, set_perms=set_permissions, changed=True,
|
module.fail_json_aws(e, msg="Error updating tags")
|
||||||
|
|
||||||
|
description = module.params.get('description')
|
||||||
|
if description and description != image['Description']:
|
||||||
|
try:
|
||||||
|
connection.modify_image_attribute(Attribute='Description ', ImageId=image_id, Description=dict(Value=description))
|
||||||
|
changed = True
|
||||||
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
|
module.fail_json_aws(e, msg="Error setting description for image %s" % image_id)
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
module.exit_json(msg="AMI updated.", changed=True,
|
||||||
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
||||||
else:
|
else:
|
||||||
module.exit_json(msg="AMI not updated.", launch_permissions=set_permissions, changed=False,
|
module.exit_json(msg="AMI not updated.", changed=False,
|
||||||
**get_ami_info(get_image_by_id(module, connection, image_id)))
|
**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))
|
|
||||||
|
|
||||||
|
|
||||||
def get_image_by_id(module, connection, image_id):
|
def get_image_by_id(module, connection, image_id):
|
||||||
|
try:
|
||||||
try:
|
try:
|
||||||
images_response = connection.describe_images(ImageIds=[image_id])
|
images_response = connection.describe_images(ImageIds=[image_id])
|
||||||
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
|
module.fail_json_aws(e, msg="Error retrieving image %s" % image_id)
|
||||||
images = images_response.get('Images')
|
images = images_response.get('Images')
|
||||||
no_images = len(images)
|
no_images = len(images)
|
||||||
if no_images == 0:
|
if no_images == 0:
|
||||||
return None
|
return None
|
||||||
if no_images == 1:
|
if no_images == 1:
|
||||||
return images[0]
|
result = images[0]
|
||||||
module.fail_json(msg="Invalid number of instances (%s) found for image_id: %s." % (str(len(images)), image_id))
|
try:
|
||||||
|
result['LaunchPermissions'] = connection.describe_image_attribute(Attribute='launchPermission', ImageId=image_id)['LaunchPermissions']
|
||||||
|
result['ProductCodes'] = connection.describe_image_attribute(Attribute='productCodes', ImageId=image_id)['ProductCodes']
|
||||||
except botocore.exceptions.ClientError as e:
|
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))
|
if e.response['Error']['Code'] != 'InvalidAMIID.Unavailable':
|
||||||
|
module.fail_json_aws(e, msg="Error retrieving image attributes" % image_id)
|
||||||
|
except botocore.exceptions.BotoCoreError as e:
|
||||||
|
module.fail_json_aws(e, msg="Error retrieving image attributes" % image_id)
|
||||||
|
return result
|
||||||
|
module.fail_json(msg="Invalid number of instances (%s) found for image_id: %s." % (str(len(images)), image_id))
|
||||||
|
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
|
||||||
|
module.fail_json_aws(e, msg="Error retrieving image by image_id")
|
||||||
|
|
||||||
|
|
||||||
def rename_item_if_exists(dict_object, attribute, new_attribute, child_node=None):
|
def rename_item_if_exists(dict_object, attribute, new_attribute, child_node=None):
|
||||||
|
@ -641,40 +669,32 @@ def main():
|
||||||
enhanced_networking=dict(type='bool'),
|
enhanced_networking=dict(type='bool'),
|
||||||
billing_products=dict(type='list'),
|
billing_products=dict(type='list'),
|
||||||
ramdisk_id=dict(),
|
ramdisk_id=dict(),
|
||||||
sriov_net_support=dict()
|
sriov_net_support=dict(),
|
||||||
|
purge_tags=dict(type='bool', default=False)
|
||||||
))
|
))
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleAWSModule(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
required_if=[
|
required_if=[
|
||||||
['state', 'absent', ['image_id']],
|
['state', 'absent', ['image_id']],
|
||||||
['state', 'present', ['name']],
|
['state', 'present', ['name']],
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
module = AnsibleModule(
|
|
||||||
argument_spec=argument_spec,
|
|
||||||
required_if=[('state', 'present', ('name',)),
|
|
||||||
('state', 'absent', ('image_id',))]
|
|
||||||
)
|
|
||||||
|
|
||||||
if not HAS_BOTO3:
|
|
||||||
module.fail_json(msg='boto3 required for this module')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
|
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)
|
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:
|
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."))
|
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':
|
if module.params.get('state') == 'absent':
|
||||||
deregister_image(module, connection)
|
deregister_image(module, connection)
|
||||||
elif module.params.get('state') == 'present':
|
elif module.params.get('state') == 'present':
|
||||||
if module.params.get('image_id') and module.params.get('launch_permissions'):
|
if module.params.get('image_id'):
|
||||||
update_image(module, connection, module.params.get('image_id'), resource)
|
update_image(module, connection, module.params.get('image_id'))
|
||||||
if not module.params.get('instance_id') and not module.params.get('device_mapping'):
|
if not module.params.get('instance_id') and not module.params.get('device_mapping'):
|
||||||
module.fail_json(msg="The parameters instance_id or device_mapping (register from EBS snapshot) are required for a new image.")
|
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)
|
create_image(module, connection)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -101,7 +101,6 @@
|
||||||
Name: '{{ ec2_ami_name }}_ami'
|
Name: '{{ ec2_ami_name }}_ami'
|
||||||
wait: yes
|
wait: yes
|
||||||
root_device_name: /dev/xvda
|
root_device_name: /dev/xvda
|
||||||
ignore_errors: true
|
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
- name: assert that image has been created
|
- name: assert that image has been created
|
||||||
|
@ -109,8 +108,7 @@
|
||||||
that:
|
that:
|
||||||
- "result.changed"
|
- "result.changed"
|
||||||
- "result.image_id.startswith('ami-')"
|
- "result.image_id.startswith('ami-')"
|
||||||
# FIXME: tags are not currently shown in the results
|
- "'Name' in result.tags and result.tags.Name == ec2_ami_name + '_ami'"
|
||||||
#- "result.tags == '{Name: {{ ec2_ami_name }}_ami}'"
|
|
||||||
|
|
||||||
- name: set image id fact for deletion later
|
- name: set image id fact for deletion later
|
||||||
set_fact:
|
set_fact:
|
||||||
|
@ -188,6 +186,8 @@
|
||||||
name: '{{ ec2_ami_name }}_ami'
|
name: '{{ ec2_ami_name }}_ami'
|
||||||
description: '{{ ec2_ami_description }}'
|
description: '{{ ec2_ami_description }}'
|
||||||
state: present
|
state: present
|
||||||
|
launch_permissions:
|
||||||
|
user_ids: []
|
||||||
tags:
|
tags:
|
||||||
Name: '{{ ec2_ami_name }}_ami'
|
Name: '{{ ec2_ami_name }}_ami'
|
||||||
root_device_name: /dev/xvda
|
root_device_name: /dev/xvda
|
||||||
|
@ -213,39 +213,37 @@
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
# FIXME: this only works if launch permissions are specified and if they are not an empty list
|
- name: test default launch permissions idempotence
|
||||||
# - name: test idempotence
|
ec2_ami:
|
||||||
# ec2_ami:
|
ec2_region: '{{ec2_region}}'
|
||||||
# ec2_region: '{{ec2_region}}'
|
ec2_access_key: '{{ec2_access_key}}'
|
||||||
# ec2_access_key: '{{ec2_access_key}}'
|
ec2_secret_key: '{{ec2_secret_key}}'
|
||||||
# ec2_secret_key: '{{ec2_secret_key}}'
|
security_token: '{{security_token}}'
|
||||||
# security_token: '{{security_token}}'
|
description: '{{ ec2_ami_description }}'
|
||||||
# description: '{{ ec2_ami_description }}'
|
state: present
|
||||||
# state: present
|
name: '{{ ec2_ami_name }}_ami'
|
||||||
# tags:
|
tags:
|
||||||
# Name: '{{ ec2_ami_name }}_ami'
|
Name: '{{ ec2_ami_name }}_ami'
|
||||||
# root_device_name: /dev/xvda
|
root_device_name: /dev/xvda
|
||||||
# image_id: '{{ result.image_id }}'
|
image_id: '{{ result.image_id }}'
|
||||||
# launch_permissions:
|
launch_permissions:
|
||||||
# user_ids:
|
user_ids: []
|
||||||
# -
|
device_mapping:
|
||||||
# device_mapping:
|
- device_name: /dev/xvda
|
||||||
# - device_name: /dev/xvda
|
volume_type: gp2
|
||||||
# volume_type: gp2
|
size: 8
|
||||||
# size: 8
|
delete_on_termination: true
|
||||||
# delete_on_termination: true
|
snapshot_id: '{{ setup_snapshot.snapshot_id }}'
|
||||||
# snapshot_id: '{{ setup_snapshot.snapshot_id }}'
|
register: result
|
||||||
# register: result
|
|
||||||
|
|
||||||
# - name: assert a new ami has been created
|
- name: assert a new ami has not been created
|
||||||
# assert:
|
assert:
|
||||||
# that:
|
that:
|
||||||
# - "not result.changed"
|
- "not result.changed"
|
||||||
# - "result.image_id.startswith('ami-')"
|
- "result.image_id.startswith('ami-')"
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
# FIXME: tags are not currently shown in the results
|
|
||||||
- name: add a tag to the AMI
|
- name: add a tag to the AMI
|
||||||
ec2_ami:
|
ec2_ami:
|
||||||
ec2_region: '{{ec2_region}}'
|
ec2_region: '{{ec2_region}}'
|
||||||
|
@ -258,14 +256,34 @@
|
||||||
name: '{{ ec2_ami_name }}_ami'
|
name: '{{ ec2_ami_name }}_ami'
|
||||||
tags:
|
tags:
|
||||||
New: Tag
|
New: Tag
|
||||||
launch_permissions:
|
|
||||||
group_names: ['all']
|
|
||||||
register: result
|
register: result
|
||||||
#
|
|
||||||
# - name: assert a tag was added
|
- name: assert a tag was added
|
||||||
# assert:
|
assert:
|
||||||
# that:
|
that:
|
||||||
# - "result.tags == '{Name: {{ ec2_ami_name }}_ami}, New: Tag'"
|
- "'Name' in result.tags and result.tags.Name == ec2_ami_name + '_ami'"
|
||||||
|
- "'New' in result.tags and result.tags.New == 'Tag'"
|
||||||
|
|
||||||
|
- name: use purge_tags to remove a tag from the AMI
|
||||||
|
ec2_ami:
|
||||||
|
ec2_region: '{{ec2_region}}'
|
||||||
|
ec2_access_key: '{{ec2_access_key}}'
|
||||||
|
ec2_secret_key: '{{ec2_secret_key}}'
|
||||||
|
security_token: '{{security_token}}'
|
||||||
|
state: present
|
||||||
|
description: '{{ ec2_ami_description }}'
|
||||||
|
image_id: '{{ result.image_id }}'
|
||||||
|
name: '{{ ec2_ami_name }}_ami'
|
||||||
|
tags:
|
||||||
|
New: Tag
|
||||||
|
purge_tags: yes
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert a tag was removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "'Name' not in result.tags"
|
||||||
|
- "'New' in result.tags and result.tags.New == 'Tag'"
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
|
@ -315,29 +333,25 @@
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
# FIXME: currently the module doesn't remove launch permissions correctly
|
- name: remove public launch permissions
|
||||||
# - name: remove public launch permissions
|
ec2_ami:
|
||||||
# ec2_ami:
|
ec2_region: '{{ec2_region}}'
|
||||||
# ec2_region: '{{ec2_region}}'
|
ec2_access_key: '{{ec2_access_key}}'
|
||||||
# ec2_access_key: '{{ec2_access_key}}'
|
ec2_secret_key: '{{ec2_secret_key}}'
|
||||||
# ec2_secret_key: '{{ec2_secret_key}}'
|
security_token: '{{security_token}}'
|
||||||
# security_token: '{{security_token}}'
|
state: present
|
||||||
# state: present
|
image_id: '{{ result.image_id }}'
|
||||||
# image_id: '{{ result.image_id }}'
|
name: '{{ ec2_ami_name }}_ami'
|
||||||
# name: '{{ ec2_ami_name }}_ami'
|
tags:
|
||||||
# tags:
|
Name: '{{ ec2_ami_name }}_ami'
|
||||||
# Name: '{{ ec2_ami_name }}_ami'
|
launch_permissions:
|
||||||
# launch_permissions:
|
group_names: []
|
||||||
# group_names:
|
register: result
|
||||||
# -
|
|
||||||
#
|
- name: assert launch permissions were updated
|
||||||
# register: result
|
assert:
|
||||||
# ignore_errors: true
|
that:
|
||||||
#
|
- "result.changed"
|
||||||
# - name: assert launch permissions were updated
|
|
||||||
# assert:
|
|
||||||
# that:
|
|
||||||
# - "result.changed"
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
|
@ -391,16 +405,13 @@
|
||||||
tags:
|
tags:
|
||||||
Name: '{{ ec2_ami_name }}_ami'
|
Name: '{{ ec2_ami_name }}_ami'
|
||||||
wait: yes
|
wait: yes
|
||||||
ignore_errors: true
|
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
# FIXME: currently deleting an already deleted image fails
|
- name: assert that image does not exist
|
||||||
# It should succeed, with changed: false
|
assert:
|
||||||
# - name: assert that image does not exist
|
that:
|
||||||
# assert:
|
- not result.changed
|
||||||
# that:
|
- not result.failed
|
||||||
# - not result.changed
|
|
||||||
# - not result.failed
|
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
Loading…
Reference in a new issue