Merge pull request #1640 from andynelson/ec2_vpc_options
Updated ec2_vpc_dhcp_options
This commit is contained in:
commit
5e63d9fb71
1 changed files with 241 additions and 94 deletions
|
@ -16,13 +16,21 @@
|
||||||
DOCUMENTATION = """
|
DOCUMENTATION = """
|
||||||
---
|
---
|
||||||
module: ec2_vpc_dhcp_options
|
module: ec2_vpc_dhcp_options
|
||||||
short_description: Ensures the DHCP options for the given VPC match what's
|
short_description: Manages DHCP Options, and can ensure the DHCP options for the given VPC match what's
|
||||||
requested
|
requested
|
||||||
description:
|
description:
|
||||||
- Converges the DHCP option set for the given VPC to the variables requested.
|
- This module removes, or creates DHCP option sets, and can associate them to a VPC.
|
||||||
|
Optionally, a new DHCP Options set can be created that converges a VPC's existing
|
||||||
|
DHCP option set with values provided.
|
||||||
|
When dhcp_options_id is provided, the module will
|
||||||
|
1. remove (with state='absent')
|
||||||
|
2. ensure tags are applied (if state='present' and tags are provided
|
||||||
|
3. attach it to a VPC (if state='present' and a vpc_id is provided.
|
||||||
If any of the optional values are missing, they will either be treated
|
If any of the optional values are missing, they will either be treated
|
||||||
as a no-op (i.e., inherit what already exists for the VPC) or a purge of
|
as a no-op (i.e., inherit what already exists for the VPC)
|
||||||
existing options. Most of the options should be self-explanatory.
|
To remove existing options while inheriting, supply an empty value
|
||||||
|
(e.g. set ntp_servers to [] if you want to remove them from the VPC's options)
|
||||||
|
Most of the options should be self-explanatory.
|
||||||
author: "Joel Thompson (@joelthompson)"
|
author: "Joel Thompson (@joelthompson)"
|
||||||
version_added: 2.1
|
version_added: 2.1
|
||||||
options:
|
options:
|
||||||
|
@ -30,37 +38,40 @@ options:
|
||||||
description:
|
description:
|
||||||
- The domain name to set in the DHCP option sets
|
- The domain name to set in the DHCP option sets
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: None
|
||||||
dns_servers:
|
dns_servers:
|
||||||
description:
|
description:
|
||||||
- A list of hosts to set the DNS servers for the VPC to. (Should be a
|
- A list of hosts to set the DNS servers for the VPC to. (Should be a
|
||||||
list of IP addresses rather than host names.)
|
list of IP addresses rather than host names.)
|
||||||
required: false
|
required: false
|
||||||
default: []
|
default: None
|
||||||
ntp_servers:
|
ntp_servers:
|
||||||
description:
|
description:
|
||||||
- List of hosts to advertise as NTP servers for the VPC.
|
- List of hosts to advertise as NTP servers for the VPC.
|
||||||
required: false
|
required: false
|
||||||
default: []
|
default: None
|
||||||
netbios_name_servers:
|
netbios_name_servers:
|
||||||
description:
|
description:
|
||||||
- List of hosts to advertise as NetBIOS servers.
|
- List of hosts to advertise as NetBIOS servers.
|
||||||
required: false
|
required: false
|
||||||
default: []
|
default: None
|
||||||
netbios_node_type:
|
netbios_node_type:
|
||||||
description:
|
description:
|
||||||
- NetBIOS node type to advertise in the DHCP options. The
|
- NetBIOS node type to advertise in the DHCP options.
|
||||||
default is 2, per AWS recommendation
|
The AWS recommendation is to use 2 (when using netbios name services)
|
||||||
http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html
|
http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html
|
||||||
required: false
|
required: false
|
||||||
default: 2
|
default: None
|
||||||
vpc_id:
|
vpc_id:
|
||||||
description:
|
description:
|
||||||
- VPC ID to associate with the requested DHCP option set
|
- VPC ID to associate with the requested DHCP option set.
|
||||||
required: true
|
If no vpc id is provided, and no matching option set is found then a new
|
||||||
|
DHCP option set is created.
|
||||||
|
required: false
|
||||||
|
default: None
|
||||||
delete_old:
|
delete_old:
|
||||||
description:
|
description:
|
||||||
- Whether to delete the old VPC DHCP option set when creating a new one.
|
- Whether to delete the old VPC DHCP option set when associating a new one.
|
||||||
This is primarily useful for debugging/development purposes when you
|
This is primarily useful for debugging/development purposes when you
|
||||||
want to quickly roll back to the old option set. Note that this setting
|
want to quickly roll back to the old option set. Note that this setting
|
||||||
will be ignored, and the old DHCP option set will be preserved, if it
|
will be ignored, and the old DHCP option set will be preserved, if it
|
||||||
|
@ -74,6 +85,28 @@ options:
|
||||||
reset them to be empty.
|
reset them to be empty.
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
tags:
|
||||||
|
description:
|
||||||
|
- Tags to be applied to a VPC options set if a new one is created, or
|
||||||
|
if the resource_id is provided. (options must match)
|
||||||
|
required: False
|
||||||
|
default: None
|
||||||
|
aliases: [ 'resource_tags']
|
||||||
|
dhcp_options_id:
|
||||||
|
description:
|
||||||
|
- The resource_id of an existing DHCP options set.
|
||||||
|
If this is specified, then it will override other settings, except tags
|
||||||
|
(which will be updated to match)
|
||||||
|
required: False
|
||||||
|
default: None
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- create/assign or remove the DHCP options.
|
||||||
|
If state is set to absent, then a DHCP options set matched either
|
||||||
|
by id, or tags and options will be removed if possible.
|
||||||
|
required: False
|
||||||
|
default: present
|
||||||
|
choices: [ 'absent', 'present' ]
|
||||||
extends_documentation_fragment: aws
|
extends_documentation_fragment: aws
|
||||||
requirements:
|
requirements:
|
||||||
- boto
|
- boto
|
||||||
|
@ -81,19 +114,26 @@ requirements:
|
||||||
|
|
||||||
RETURN = """
|
RETURN = """
|
||||||
new_options:
|
new_options:
|
||||||
description: The new DHCP options associated with your VPC
|
description: The DHCP options created, associated or found
|
||||||
returned: changed
|
returned: when appropriate
|
||||||
type: dict
|
type: dict
|
||||||
sample:
|
sample:
|
||||||
domain-name-servers:
|
domain-name-servers:
|
||||||
- 10.0.0.1
|
- 10.0.0.1
|
||||||
- 10.0.1.1
|
- 10.0.1.1
|
||||||
netbois-name-servers:
|
netbois-name-servers:
|
||||||
- 10.0.0.1
|
- 10.0.0.1
|
||||||
- 10.0.1.1
|
- 10.0.1.1
|
||||||
ntp-servers: None
|
netbios-node-type: 2
|
||||||
netbios-node-type: 2
|
domain-name: "my.example.com"
|
||||||
domain-name: "my.example.com"
|
dhcp_options_id:
|
||||||
|
description: The aws resource id of the primary DCHP options set created, found or removed
|
||||||
|
type: string
|
||||||
|
returned: when available
|
||||||
|
changed:
|
||||||
|
description: Whether the dhcp options were changed
|
||||||
|
type: bool
|
||||||
|
returned: always
|
||||||
"""
|
"""
|
||||||
|
|
||||||
EXAMPLES = """
|
EXAMPLES = """
|
||||||
|
@ -127,105 +167,212 @@ EXAMPLES = """
|
||||||
vpc_id: vpc-123456
|
vpc_id: vpc-123456
|
||||||
inherit_existing: True
|
inherit_existing: True
|
||||||
delete_old: False
|
delete_old: False
|
||||||
|
|
||||||
|
|
||||||
|
## Create a DHCP option set with 4.4.4.4 and 8.8.8.8 as the specified DNS servers, with tags
|
||||||
|
## but do not assign to a VPC
|
||||||
|
- ec2_vpc_dhcp_options:
|
||||||
|
region: us-east-1
|
||||||
|
dns_servers:
|
||||||
|
- 4.4.4.4
|
||||||
|
- 8.8.8.8
|
||||||
|
tags:
|
||||||
|
Name: google servers
|
||||||
|
Environment: Test
|
||||||
|
|
||||||
|
## Delete a DHCP options set that matches the tags and options specified
|
||||||
|
- ec2_vpc_dhcp_options:
|
||||||
|
region: us-east-1
|
||||||
|
dns_servers:
|
||||||
|
- 4.4.4.4
|
||||||
|
- 8.8.8.8
|
||||||
|
tags:
|
||||||
|
Name: google servers
|
||||||
|
Environment: Test
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
## Associate a DHCP options set with a VPC by ID
|
||||||
|
- ec2_vpc_dhcp_options:
|
||||||
|
region: us-east-1
|
||||||
|
dhcp_options_id: dopt-12345678
|
||||||
|
vpc_id: vpc-123456
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import boto.vpc
|
import boto.vpc
|
||||||
|
import boto.ec2
|
||||||
|
from boto.exception import EC2ResponseError
|
||||||
import socket
|
import socket
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
def _get_associated_dhcp_options(vpc_id, vpc_connection):
|
def get_resource_tags(vpc_conn, resource_id):
|
||||||
|
return dict((t.name, t.value) for t in vpc_conn.get_all_tags(filters={'resource-id': resource_id}))
|
||||||
|
|
||||||
|
def ensure_tags(vpc_conn, resource_id, tags, add_only, check_mode):
|
||||||
|
try:
|
||||||
|
cur_tags = get_resource_tags(vpc_conn, resource_id)
|
||||||
|
if tags == cur_tags:
|
||||||
|
return {'changed': False, 'tags': cur_tags}
|
||||||
|
|
||||||
|
to_delete = dict((k, cur_tags[k]) for k in cur_tags if k not in tags)
|
||||||
|
if to_delete and not add_only:
|
||||||
|
vpc_conn.delete_tags(resource_id, to_delete, dry_run=check_mode)
|
||||||
|
|
||||||
|
to_add = dict((k, tags[k]) for k in tags if k not in cur_tags)
|
||||||
|
if to_add:
|
||||||
|
vpc_conn.create_tags(resource_id, to_add, dry_run=check_mode)
|
||||||
|
|
||||||
|
latest_tags = get_resource_tags(vpc_conn, resource_id)
|
||||||
|
return {'changed': True, 'tags': latest_tags}
|
||||||
|
except EC2ResponseError as e:
|
||||||
|
module.fail_json(msg=get_error_message(e.args[2]))
|
||||||
|
|
||||||
|
def fetch_dhcp_options_for_vpc(vpc_conn, vpc_id):
|
||||||
"""
|
"""
|
||||||
Returns the DHCP options object currently associated with the requested VPC ID using the VPC
|
Returns the DHCP options object currently associated with the requested VPC ID using the VPC
|
||||||
connection variable.
|
connection variable.
|
||||||
"""
|
"""
|
||||||
vpcs = vpc_connection.get_all_vpcs(vpc_ids=[vpc_id])
|
vpcs = vpc_conn.get_all_vpcs(vpc_ids=[vpc_id])
|
||||||
if len(vpcs) != 1:
|
if len(vpcs) != 1 or vpcs[0].dhcp_options_id == "default":
|
||||||
return None
|
return None
|
||||||
dhcp_options = vpc_connection.get_all_dhcp_options(dhcp_options_ids=[vpcs[0].dhcp_options_id])
|
dhcp_options = vpc_conn.get_all_dhcp_options(dhcp_options_ids=[vpcs[0].dhcp_options_id])
|
||||||
if len(dhcp_options) != 1:
|
if len(dhcp_options) != 1:
|
||||||
return None
|
return None
|
||||||
return dhcp_options[0]
|
return dhcp_options[0]
|
||||||
|
|
||||||
|
def match_dhcp_options(vpc_conn, tags=None, options=None):
|
||||||
|
"""
|
||||||
|
Finds a DHCP Options object that optionally matches the tags and options provided
|
||||||
|
"""
|
||||||
|
dhcp_options = vpc_conn.get_all_dhcp_options()
|
||||||
|
for dopts in dhcp_options:
|
||||||
|
if (not tags) or get_resource_tags(vpc_conn, dopts.id) == tags:
|
||||||
|
if (not options) or dopts.options == options:
|
||||||
|
return(True, dopts)
|
||||||
|
return(False, None)
|
||||||
|
|
||||||
def _get_vpcs_by_dhcp_options(dhcp_options_id, vpc_connection):
|
def remove_dhcp_options_by_id(vpc_conn, dhcp_options_id):
|
||||||
return vpc_connection.get_all_vpcs(filters={'dhcpOptionsId': dhcp_options_id})
|
associations = vpc_conn.get_all_vpcs(filters={'dhcpOptionsId': dhcp_options_id})
|
||||||
|
if len(associations) > 0:
|
||||||
|
return False
|
||||||
def _get_updated_option(requested, existing, inherit):
|
|
||||||
if inherit and (not requested or requested == ['']):
|
|
||||||
return existing
|
|
||||||
else:
|
else:
|
||||||
return requested
|
vpc_conn.delete_dhcp_options(dhcp_options_id)
|
||||||
|
return True
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = ec2_argument_spec()
|
argument_spec = ec2_argument_spec()
|
||||||
argument_spec.update(dict(
|
argument_spec.update(dict(
|
||||||
domain_name=dict(type='str', default=''),
|
dhcp_options_id=dict(type='str', default=None),
|
||||||
dns_servers=dict(type='list', default=[]),
|
domain_name=dict(type='str', default=None),
|
||||||
ntp_servers=dict(type='list', default=[]),
|
dns_servers=dict(type='list', default=None),
|
||||||
netbios_name_servers=dict(type='list', default=[]),
|
ntp_servers=dict(type='list', default=None),
|
||||||
netbios_node_type=dict(type='int', default=2),
|
netbios_name_servers=dict(type='list', default=None),
|
||||||
vpc_id=dict(type='str', required=True),
|
netbios_node_type=dict(type='int', default=None),
|
||||||
|
vpc_id=dict(type='str', default=None),
|
||||||
delete_old=dict(type='bool', default=True),
|
delete_old=dict(type='bool', default=True),
|
||||||
inherit_existing=dict(type='bool', default=False)
|
inherit_existing=dict(type='bool', default=False),
|
||||||
|
tags=dict(type='dict', default=None, aliases=['resource_tags']),
|
||||||
|
state=dict(type='str', default='present', choices=['present', 'absent'])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
|
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
|
||||||
params = module.params
|
params = module.params
|
||||||
|
found = False
|
||||||
|
changed = False
|
||||||
|
new_options = collections.defaultdict(lambda: None)
|
||||||
|
|
||||||
|
|
||||||
region, ec2_url, boto_params = get_aws_connection_info(module)
|
region, ec2_url, boto_params = get_aws_connection_info(module)
|
||||||
connection = connect_to_aws(boto.vpc, region, **boto_params)
|
connection = connect_to_aws(boto.vpc, region, **boto_params)
|
||||||
|
|
||||||
inherit_existing = params['inherit_existing']
|
existing_options = None
|
||||||
|
|
||||||
existing_options = _get_associated_dhcp_options(params['vpc_id'], connection)
|
# First check if we were given a dhcp_options_id
|
||||||
new_options = collections.defaultdict(lambda: None)
|
if not params['dhcp_options_id']:
|
||||||
|
# No, so create new_options from the parameters
|
||||||
new_options['domain-name-servers'] = _get_updated_option( params['dns_servers'],
|
if params['dns_servers'] != None:
|
||||||
existing_options.options.get('domain-name-servers'), inherit_existing)
|
new_options['domain-name-servers'] = params['dns_servers']
|
||||||
|
if params['netbios_name_servers'] != None:
|
||||||
new_options['netbios-name-servers'] = _get_updated_option(params['netbios_name_servers'],
|
new_options['netbios-name-servers'] = params['netbios_name_servers']
|
||||||
existing_options.options.get('netbios-name-servers'), inherit_existing)
|
if params['ntp_servers'] != None:
|
||||||
|
new_options['ntp-servers'] = params['ntp_servers']
|
||||||
|
if params['domain_name'] != None:
|
||||||
new_options['ntp-servers'] = _get_updated_option(params['ntp_servers'],
|
# needs to be a list for comparison with boto objects later
|
||||||
existing_options.options.get('ntp-servers'), inherit_existing)
|
new_options['domain-name'] = [ params['domain_name'] ]
|
||||||
|
if params['netbios_node_type'] != None:
|
||||||
# HACK: Why do I make the next two lists? The boto api returns a list if present, so
|
# needs to be a list for comparison with boto objects later
|
||||||
# I need this to properly compare so == works.
|
new_options['netbios-node-type'] = [ str(params['netbios_node_type']) ]
|
||||||
|
# If we were given a vpc_id then we need to look at the options on that
|
||||||
# HACK: netbios-node-type is an int, but boto returns a string. So, asking for an int from Ansible
|
if params['vpc_id']:
|
||||||
# for data validation, but still need to cast it to a string
|
existing_options = fetch_dhcp_options_for_vpc(connection, params['vpc_id'])
|
||||||
new_options['netbios-node-type'] = _get_updated_option(
|
# if we've been asked to inherit existing options, do that now
|
||||||
[str(params['netbios_node_type'])], existing_options.options.get('netbios-node-type'),
|
if params['inherit_existing']:
|
||||||
inherit_existing)
|
if existing_options:
|
||||||
|
for option in [ 'domain-name-servers', 'netbios-name-servers', 'ntp-servers', 'domain-name', 'netbios-node-type']:
|
||||||
new_options['domain-name'] = _get_updated_option(
|
if existing_options.options.get(option) and new_options[option] != [] and (not new_options[option] or [''] == new_options[option]):
|
||||||
[params['domain_name']], existing_options.options.get('domain-name'), inherit_existing)
|
new_options[option] = existing_options.options.get(option)
|
||||||
|
|
||||||
if existing_options and new_options == existing_options.options:
|
# Do the vpc's dhcp options already match what we're asked for? if so we are done
|
||||||
module.exit_json(changed=False)
|
if existing_options and new_options == existing_options.options:
|
||||||
|
module.exit_json(changed=changed, new_options=new_options, dhcp_options_id=existing_options.id)
|
||||||
if new_options['netbios-node-type']:
|
|
||||||
new_options['netbios-node-type'] = new_options['netbios-node-type'][0]
|
# If no vpc_id was given, or the options don't match then look for an existing set using tags
|
||||||
|
found, dhcp_option = match_dhcp_options(connection, params['tags'], new_options)
|
||||||
if new_options['domain-name']:
|
|
||||||
new_options['domain-name'] = new_options['domain-name'][0]
|
# Now let's cover the case where there are existing options that we were told about by id
|
||||||
|
# If a dhcp_options_id was supplied we don't look at options inside, just set tags (if given)
|
||||||
if not module.check_mode:
|
else:
|
||||||
dhcp_option = connection.create_dhcp_options(new_options['domain-name'],
|
supplied_options = connection.get_all_dhcp_options(filters={'dhcp-options-id':params['dhcp_options_id']})
|
||||||
new_options['domain-name-servers'], new_options['ntp-servers'],
|
if len(supplied_options) != 1:
|
||||||
new_options['netbios-name-servers'], new_options['netbios-node-type'])
|
if params['state'] != 'absent':
|
||||||
|
module.fail_json(msg=" a dhcp_options_id was supplied, but does not exist")
|
||||||
|
else:
|
||||||
|
found = True
|
||||||
|
dhcp_option = supplied_options[0]
|
||||||
|
if params['state'] != 'absent' and params['tags']:
|
||||||
|
ensure_tags(connection, dhcp_option.id, params['tags'], False, module.check_mode)
|
||||||
|
|
||||||
|
# Now we have the dhcp options set, let's do the necessary
|
||||||
|
|
||||||
|
# if we found options we were asked to remove then try to do so
|
||||||
|
if params['state'] == 'absent':
|
||||||
|
if not module.check_mode:
|
||||||
|
if found:
|
||||||
|
changed = remove_dhcp_options_by_id(connection, dhcp_option.id)
|
||||||
|
module.exit_json(changed=changed, new_options={})
|
||||||
|
|
||||||
|
# otherwise if we haven't found the required options we have something to do
|
||||||
|
elif not module.check_mode and not found:
|
||||||
|
|
||||||
|
# create some dhcp options if we weren't able to use existing ones
|
||||||
|
if not found:
|
||||||
|
# Convert netbios-node-type and domain-name back to strings
|
||||||
|
if new_options['netbios-node-type']:
|
||||||
|
new_options['netbios-node-type'] = new_options['netbios-node-type'][0]
|
||||||
|
if new_options['domain-name']:
|
||||||
|
new_options['domain-name'] = new_options['domain-name'][0]
|
||||||
|
|
||||||
|
# create the new dhcp options set requested
|
||||||
|
dhcp_option = connection.create_dhcp_options(
|
||||||
|
new_options['domain-name'],
|
||||||
|
new_options['domain-name-servers'],
|
||||||
|
new_options['ntp-servers'],
|
||||||
|
new_options['netbios-name-servers'],
|
||||||
|
new_options['netbios-node-type'])
|
||||||
|
changed = True
|
||||||
|
if params['tags']:
|
||||||
|
ensure_tags(connection, dhcp_option.id, params['tags'], False, module.check_mode)
|
||||||
|
|
||||||
|
# If we were given a vpc_id, then attach the options we now have to that before we finish
|
||||||
|
if params['vpc_id'] and not module.check_mode:
|
||||||
|
changed = True
|
||||||
connection.associate_dhcp_options(dhcp_option.id, params['vpc_id'])
|
connection.associate_dhcp_options(dhcp_option.id, params['vpc_id'])
|
||||||
|
# and remove old ones if that was requested
|
||||||
if params['delete_old'] and existing_options:
|
if params['delete_old'] and existing_options:
|
||||||
other_vpcs = _get_vpcs_by_dhcp_options(existing_options.id, connection)
|
remove_dhcp_options_by_id(connection, existing_options.id)
|
||||||
if len(other_vpcs) == 0 or (len(other_vpcs) == 1 and other_vpcs[0].id == params['vpc_id']):
|
|
||||||
connection.delete_dhcp_options(existing_options.id)
|
|
||||||
|
|
||||||
module.exit_json(changed=True, new_options=new_options)
|
module.exit_json(changed=changed, new_options=new_options, dhcp_options_id=dhcp_option.id)
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import *
|
from ansible.module_utils.basic import *
|
||||||
|
|
Loading…
Reference in a new issue