|
|
@ -18,7 +18,9 @@ DOCUMENTATION = '''
|
|
|
|
module: ec2_eni
|
|
|
|
module: ec2_eni
|
|
|
|
short_description: Create and optionally attach an Elastic Network Interface (ENI) to an instance
|
|
|
|
short_description: Create and optionally attach an Elastic Network Interface (ENI) to an instance
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- Create and optionally attach an Elastic Network Interface (ENI) to an instance. If an ENI ID is provided, an attempt is made to update the existing ENI. By passing 'None' as the instance_id, an ENI can be detached from an instance.
|
|
|
|
- Create and optionally attach an Elastic Network Interface (ENI) to an
|
|
|
|
|
|
|
|
instance. If an ENI ID is provided, an attempt is made to update the
|
|
|
|
|
|
|
|
existing ENI. By passing state=detached, an ENI can be detached from its instance.
|
|
|
|
version_added: "2.0"
|
|
|
|
version_added: "2.0"
|
|
|
|
author: "Rob White (@wimnat)"
|
|
|
|
author: "Rob White (@wimnat)"
|
|
|
|
options:
|
|
|
|
options:
|
|
|
@ -29,7 +31,8 @@ options:
|
|
|
|
default: null
|
|
|
|
default: null
|
|
|
|
instance_id:
|
|
|
|
instance_id:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- Instance ID that you wish to attach ENI to. To detach an ENI from an instance, use 'None'.
|
|
|
|
- Instance ID that you wish to attach ENI to, if None the new ENI will be
|
|
|
|
|
|
|
|
created in detached state, existing ENI will keep current attachment state.
|
|
|
|
required: false
|
|
|
|
required: false
|
|
|
|
default: null
|
|
|
|
default: null
|
|
|
|
private_ip_address:
|
|
|
|
private_ip_address:
|
|
|
@ -54,10 +57,10 @@ options:
|
|
|
|
default: null
|
|
|
|
default: null
|
|
|
|
state:
|
|
|
|
state:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- Create or delete ENI.
|
|
|
|
- Create, delete or detach ENI from its instance.
|
|
|
|
required: false
|
|
|
|
required: false
|
|
|
|
default: present
|
|
|
|
default: present
|
|
|
|
choices: [ 'present', 'absent' ]
|
|
|
|
choices: [ 'present', 'absent', 'detached' ]
|
|
|
|
device_index:
|
|
|
|
device_index:
|
|
|
|
description:
|
|
|
|
description:
|
|
|
|
- The index of the device for the network interface attachment on the instance.
|
|
|
|
- The index of the device for the network interface attachment on the instance.
|
|
|
@ -239,7 +242,7 @@ def create_eni(connection, vpc_id, module):
|
|
|
|
changed = False
|
|
|
|
changed = False
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
eni = compare_eni(connection, module)
|
|
|
|
eni = find_eni(connection, module)
|
|
|
|
if eni is None:
|
|
|
|
if eni is None:
|
|
|
|
eni = connection.create_network_interface(subnet_id, private_ip_address, description, security_groups)
|
|
|
|
eni = connection.create_network_interface(subnet_id, private_ip_address, description, security_groups)
|
|
|
|
if instance_id is not None:
|
|
|
|
if instance_id is not None:
|
|
|
@ -274,21 +277,13 @@ def create_eni(connection, vpc_id, module):
|
|
|
|
module.exit_json(changed=changed, interface=get_eni_info(eni))
|
|
|
|
module.exit_json(changed=changed, interface=get_eni_info(eni))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def modify_eni(connection, vpc_id, module, looked_up_eni_id):
|
|
|
|
def modify_eni(connection, vpc_id, module, eni):
|
|
|
|
|
|
|
|
|
|
|
|
if looked_up_eni_id is None:
|
|
|
|
|
|
|
|
eni_id = module.params.get("eni_id")
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
eni_id = looked_up_eni_id
|
|
|
|
|
|
|
|
instance_id = module.params.get("instance_id")
|
|
|
|
instance_id = module.params.get("instance_id")
|
|
|
|
if instance_id == 'None':
|
|
|
|
do_detach = module.params.get('state') == 'detached'
|
|
|
|
instance_id = None
|
|
|
|
|
|
|
|
do_detach = True
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
do_detach = False
|
|
|
|
|
|
|
|
device_index = module.params.get("device_index")
|
|
|
|
device_index = module.params.get("device_index")
|
|
|
|
description = module.params.get('description')
|
|
|
|
description = module.params.get('description')
|
|
|
|
security_groups = get_ec2_security_group_ids_from_names(module.params.get('security_groups'), connection, vpc_id=vpc_id, boto3=False)
|
|
|
|
security_groups = module.params.get('security_groups')
|
|
|
|
force_detach = module.params.get("force_detach")
|
|
|
|
force_detach = module.params.get("force_detach")
|
|
|
|
source_dest_check = module.params.get("source_dest_check")
|
|
|
|
source_dest_check = module.params.get("source_dest_check")
|
|
|
|
delete_on_termination = module.params.get("delete_on_termination")
|
|
|
|
delete_on_termination = module.params.get("delete_on_termination")
|
|
|
@ -297,28 +292,23 @@ def modify_eni(connection, vpc_id, module, looked_up_eni_id):
|
|
|
|
changed = False
|
|
|
|
changed = False
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
# Get the eni with the eni_id specified
|
|
|
|
|
|
|
|
eni_result_set = connection.get_all_network_interfaces(eni_id)
|
|
|
|
|
|
|
|
eni = eni_result_set[0]
|
|
|
|
|
|
|
|
if description is not None:
|
|
|
|
if description is not None:
|
|
|
|
if eni.description != description:
|
|
|
|
if eni.description != description:
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "description", description)
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "description", description)
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
if security_groups is not None:
|
|
|
|
if len(security_groups) > 0:
|
|
|
|
if sorted(get_sec_group_list(eni.groups)) != sorted(security_groups):
|
|
|
|
groups = get_ec2_security_group_ids_from_names(security_groups, connection, vpc_id=vpc_id, boto3=False)
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "groupSet", security_groups)
|
|
|
|
if sorted(get_sec_group_list(eni.groups)) != sorted(groups):
|
|
|
|
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "groupSet", groups)
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
if source_dest_check is not None:
|
|
|
|
if source_dest_check is not None:
|
|
|
|
if eni.source_dest_check != source_dest_check:
|
|
|
|
if eni.source_dest_check != source_dest_check:
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "sourceDestCheck", source_dest_check)
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "sourceDestCheck", source_dest_check)
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
if delete_on_termination is not None:
|
|
|
|
if delete_on_termination is not None and eni.attachment is not None:
|
|
|
|
if eni.attachment is not None:
|
|
|
|
|
|
|
|
if eni.attachment.delete_on_termination is not delete_on_termination:
|
|
|
|
if eni.attachment.delete_on_termination is not delete_on_termination:
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "deleteOnTermination", delete_on_termination, eni.attachment.id)
|
|
|
|
connection.modify_network_interface_attribute(eni.id, "deleteOnTermination", delete_on_termination, eni.attachment.id)
|
|
|
|
changed = True
|
|
|
|
changed = True
|
|
|
|
else:
|
|
|
|
|
|
|
|
module.fail_json(msg="Can not modify delete_on_termination as the interface is not attached")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current_secondary_addresses = [i.private_ip_address for i in eni.private_ip_addresses if not i.primary]
|
|
|
|
current_secondary_addresses = [i.private_ip_address for i in eni.private_ip_addresses if not i.primary]
|
|
|
|
if secondary_private_ip_addresses is not None:
|
|
|
|
if secondary_private_ip_addresses is not None:
|
|
|
@ -337,11 +327,6 @@ def modify_eni(connection, vpc_id, module, looked_up_eni_id):
|
|
|
|
secondary_addresses_to_remove_count = current_secondary_address_count - secondary_private_ip_address_count
|
|
|
|
secondary_addresses_to_remove_count = current_secondary_address_count - secondary_private_ip_address_count
|
|
|
|
connection.unassign_private_ip_addresses(network_interface_id=eni.id, private_ip_addresses=current_secondary_addresses[:secondary_addresses_to_remove_count], dry_run=False)
|
|
|
|
connection.unassign_private_ip_addresses(network_interface_id=eni.id, private_ip_addresses=current_secondary_addresses[:secondary_addresses_to_remove_count], dry_run=False)
|
|
|
|
|
|
|
|
|
|
|
|
if eni.attachment is not None and instance_id is None and do_detach is True:
|
|
|
|
|
|
|
|
eni.detach(force_detach)
|
|
|
|
|
|
|
|
wait_for_eni(eni, "detached")
|
|
|
|
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
if instance_id is not None:
|
|
|
|
if instance_id is not None:
|
|
|
|
eni.attach(instance_id, device_index)
|
|
|
|
eni.attach(instance_id, device_index)
|
|
|
|
wait_for_eni(eni, "attached")
|
|
|
|
wait_for_eni(eni, "attached")
|
|
|
@ -384,21 +369,36 @@ def delete_eni(connection, module):
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compare_eni(connection, module):
|
|
|
|
def detach_eni(connection, module):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eni = find_eni(connection, module)
|
|
|
|
|
|
|
|
if eni.attachment is not None:
|
|
|
|
|
|
|
|
eni.detach(force_detach)
|
|
|
|
|
|
|
|
wait_for_eni(eni, "detached")
|
|
|
|
|
|
|
|
eni.update()
|
|
|
|
|
|
|
|
module.exit_json(changed=True, interface=get_eni_info(eni))
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
module.exit_json(changed=False, interface=get_eni_info(eni))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_eni(connection, module):
|
|
|
|
|
|
|
|
|
|
|
|
eni_id = module.params.get("eni_id")
|
|
|
|
eni_id = module.params.get("eni_id")
|
|
|
|
subnet_id = module.params.get('subnet_id')
|
|
|
|
subnet_id = module.params.get('subnet_id')
|
|
|
|
private_ip_address = module.params.get('private_ip_address')
|
|
|
|
private_ip_address = module.params.get('private_ip_address')
|
|
|
|
description = module.params.get('description')
|
|
|
|
|
|
|
|
security_groups = module.params.get('security_groups')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
all_eni = connection.get_all_network_interfaces(eni_id)
|
|
|
|
filters = {}
|
|
|
|
|
|
|
|
if private_ip_address:
|
|
|
|
for eni in all_eni:
|
|
|
|
filters['private-ip-address'] = private_ip_address
|
|
|
|
remote_security_groups = get_sec_group_list(eni.groups)
|
|
|
|
if subnet_id:
|
|
|
|
if (eni.subnet_id == subnet_id) and (eni.private_ip_address == private_ip_address) and (eni.description == description) and (sorted(remote_security_groups) == sorted(security_groups)):
|
|
|
|
filters['subnet-id'] = subnet_id
|
|
|
|
return eni
|
|
|
|
|
|
|
|
|
|
|
|
eni_result = connection.get_all_network_interfaces(eni_id, filters=filters)
|
|
|
|
|
|
|
|
if len(eni_result) > 0:
|
|
|
|
|
|
|
|
return eni_result[0]
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
except BotoServerError as e:
|
|
|
|
except BotoServerError as e:
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
@ -424,22 +424,6 @@ def _get_vpc_id(connection, module, subnet_id):
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_eni_id_by_ip(connection, module):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
subnet_id = module.params.get('subnet_id')
|
|
|
|
|
|
|
|
private_ip_address = module.params.get('private_ip_address')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
all_eni = connection.get_all_network_interfaces(filters={'private-ip-address': private_ip_address, 'subnet-id': subnet_id})
|
|
|
|
|
|
|
|
except BotoServerError as e:
|
|
|
|
|
|
|
|
module.fail_json(msg=e.message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if all_eni:
|
|
|
|
|
|
|
|
return all_eni[0].id
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def main():
|
|
|
|
argument_spec = ec2_argument_spec()
|
|
|
|
argument_spec = ec2_argument_spec()
|
|
|
|
argument_spec.update(
|
|
|
|
argument_spec.update(
|
|
|
@ -451,7 +435,7 @@ def main():
|
|
|
|
description=dict(type='str'),
|
|
|
|
description=dict(type='str'),
|
|
|
|
security_groups=dict(default=[], type='list'),
|
|
|
|
security_groups=dict(default=[], type='list'),
|
|
|
|
device_index=dict(default=0, type='int'),
|
|
|
|
device_index=dict(default=0, type='int'),
|
|
|
|
state=dict(default='present', choices=['present', 'absent']),
|
|
|
|
state=dict(default='present', choices=['present', 'absent', 'detached']),
|
|
|
|
force_detach=dict(default='no', type='bool'),
|
|
|
|
force_detach=dict(default='no', type='bool'),
|
|
|
|
source_dest_check=dict(default=None, type='bool'),
|
|
|
|
source_dest_check=dict(default=None, type='bool'),
|
|
|
|
delete_on_termination=dict(default=None, type='bool'),
|
|
|
|
delete_on_termination=dict(default=None, type='bool'),
|
|
|
@ -467,6 +451,7 @@ def main():
|
|
|
|
required_if=([
|
|
|
|
required_if=([
|
|
|
|
('state', 'present', ['subnet_id']),
|
|
|
|
('state', 'present', ['subnet_id']),
|
|
|
|
('state', 'absent', ['eni_id']),
|
|
|
|
('state', 'absent', ['eni_id']),
|
|
|
|
|
|
|
|
('state', 'detached', ['eni_id']),
|
|
|
|
])
|
|
|
|
])
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
@ -491,16 +476,19 @@ def main():
|
|
|
|
if state == 'present':
|
|
|
|
if state == 'present':
|
|
|
|
subnet_id = module.params.get("subnet_id")
|
|
|
|
subnet_id = module.params.get("subnet_id")
|
|
|
|
vpc_id = _get_vpc_id(vpc_connection, module, subnet_id)
|
|
|
|
vpc_id = _get_vpc_id(vpc_connection, module, subnet_id)
|
|
|
|
# If private_ip_address is not None, look up to see if an ENI already exists with that IP
|
|
|
|
|
|
|
|
if eni_id is None and private_ip_address is not None:
|
|
|
|
eni = find_eni(connection, module)
|
|
|
|
eni_id = get_eni_id_by_ip(connection, module)
|
|
|
|
if eni is None:
|
|
|
|
if eni_id is None:
|
|
|
|
|
|
|
|
create_eni(connection, vpc_id, module)
|
|
|
|
create_eni(connection, vpc_id, module)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
modify_eni(connection, vpc_id, module, eni_id)
|
|
|
|
modify_eni(connection, vpc_id, module, eni)
|
|
|
|
|
|
|
|
|
|
|
|
elif state == 'absent':
|
|
|
|
elif state == 'absent':
|
|
|
|
delete_eni(connection, module)
|
|
|
|
delete_eni(connection, module)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elif state == 'detached':
|
|
|
|
|
|
|
|
detach_eni(connection, module)
|
|
|
|
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import *
|
|
|
|
from ansible.module_utils.basic import *
|
|
|
|
from ansible.module_utils.ec2 import *
|
|
|
|
from ansible.module_utils.ec2 import *
|
|
|
|
|
|
|
|
|
|
|
|