Merge pull request #31838 from lomeroe/boto_vpc-subnet_present-route_table

allow boto_vpc.subnet_present state to manage a subnet's explicitly associated route table
This commit is contained in:
Nicole Thomas 2016-03-14 09:39:18 -06:00
commit 86a3fc24ef
2 changed files with 147 additions and 9 deletions

View File

@ -128,14 +128,14 @@ def __virtual__():
if not HAS_BOTO:
return (False, 'The boto_vpc module could not be loaded: boto libraries not found')
elif _LooseVersion(boto.__version__) < _LooseVersion(required_boto_version):
return (False, 'The boto_vpc module could not be loaded: boto library is not required version 2.8.0')
return (False, 'The boto_vpc module could not be loaded: boto library version 2.8.0 is required')
required_boto3_version = '1.2.6'
# the boto_vpc execution module relies on the create_nat_gateway() method
# which was added in boto3 1.2.6
if not HAS_BOTO3:
return (False, 'The boto_vpc module could not be loaded: boto3 libraries not found')
elif _LooseVersion(boto3.__version__) < _LooseVersion(required_boto3_version):
return (False, 'The boto_vpc module could not be loaded: boto3 library is not required version 1.2.6')
return (False, 'The boto_vpc module could not be loaded: boto3 library version 1.2.6 is required')
return True
@ -966,8 +966,15 @@ def describe_subnet(subnet_id=None, subnet_name=None, region=None,
return {'subnet': None}
log.debug('Found subnet: {0}'.format(subnet.id))
keys = ('id', 'cidr_block', 'availability_zone', 'tags')
return {'subnet': dict((k, getattr(subnet, k)) for k in keys)}
keys = ('id', 'cidr_block', 'availability_zone', 'tags', 'vpc_id')
ret = {'subnet': dict((k, getattr(subnet, k)) for k in keys)}
explicit_route_table_assoc = _get_subnet_explicit_route_table(ret['subnet']['id'],
ret['subnet']['vpc_id'],
conn=None, region=region,
key=key, keyid=keyid, profile=profile)
if explicit_route_table_assoc:
ret['subnet']['explicit_route_table_association_id'] = explicit_route_table_assoc
return ret
def describe_subnets(subnet_ids=None, subnet_names=None, vpc_id=None, cidr=None,
@ -1021,12 +1028,15 @@ def describe_subnets(subnet_ids=None, subnet_names=None, vpc_id=None, cidr=None,
return {'subnets': None}
subnets_list = []
keys = ('id', 'cidr_block', 'availability_zone', 'tags')
keys = ('id', 'cidr_block', 'availability_zone', 'tags', 'vpc_id')
for item in subnets:
subnet = {}
for key in keys:
if hasattr(item, key):
subnet[key] = getattr(item, key)
explicit_route_table_assoc = _get_subnet_explicit_route_table(subnet['id'], subnet['vpc_id'], conn=conn)
if explicit_route_table_assoc:
subnet['explicit_route_table_association_id'] = explicit_route_table_assoc
subnets_list.append(subnet)
return {'subnets': subnets_list}
@ -2541,3 +2551,20 @@ def _key_remap(key, keys, item):
element[r_outkey] = r_item.get(r_inkey)
elements_list.append(element)
return elements_list
def _get_subnet_explicit_route_table(subnet_id, vpc_id, conn=None, region=None, key=None, keyid=None, profile=None):
'''
helper function to find subnet explicit route table associations
.. versionadded:: Carbon
'''
if not conn:
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
if conn:
vpc_route_tables = conn.get_all_route_tables(filters={'vpc_id': vpc_id})
for vpc_route_table in vpc_route_tables:
for rt_association in vpc_route_table.associations:
if rt_association.subnet_id == subnet_id and not rt_association.main:
return rt_association.id
return None

View File

@ -433,8 +433,11 @@ def dhcp_options_absent(name=None, dhcp_options_id=None, region=None, key=None,
def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None,
availability_zone=None, tags=None, region=None,
key=None, keyid=None, profile=None):
availability_zone=None, tags=None,
region=None, key=None,
keyid=None, profile=None,
route_table_id=None, route_table_name=None):
'''
Ensure a subnet exists.
@ -459,6 +462,18 @@ def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None,
tags
A list of tags.
route_table_id
A route table ID to explicitly associate the subnet with. If both route_table_id
and route_table_name are specified, route_table_id will take precedence.
.. versionadded:: Carbon
route_table_name
A route table name to explicitly associate the subnet with. If both route_table_id
and route_table_name are specified, route_table_id will take precedence.
.. versionadded:: Carbon
region
Region to connect to.
@ -488,6 +503,40 @@ def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None,
ret['comment'] = 'Failed to create subnet: {0}.'.format(r['error']['message'])
return ret
route_table_desc = None
_describe = None
rtid = None
if route_table_id or route_table_name:
rt = None
route_table_found = False
if route_table_id:
rtid = route_table_id
rt = __salt__['boto_vpc.route_table_exists'](route_table_id=route_table_id,
region=region, key=key, keyid=keyid,
profile=profile)
elif route_table_name:
rtid = route_table_name
rt = __salt__['boto_vpc.route_table_exists'](route_table_name=route_table_name,
region=region, key=key, keyid=keyid,
profile=profile)
if rt:
if 'exists' in rt:
if rt['exists']:
if route_table_id:
route_table_found = True
route_table_desc = __salt__['boto_vpc.describe_route_table'](route_table_id=route_table_id,
region=region, key=key, keyid=keyid,
profile=profile)
elif route_table_name:
route_table_found = True
route_table_desc = __salt__['boto_vpc.describe_route_table'](route_table_name=route_table_name,
region=region, key=key, keyid=keyid,
profile=profile)
if not route_table_found:
ret['result'] = False
ret['comment'] = 'The specified route table {0} could not be found.'.format(rtid)
return ret
if not r.get('exists'):
if __opts__['test']:
ret['comment'] = 'Subnet {0} is set to be created.'.format(name)
@ -509,11 +558,73 @@ def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None,
ret['changes']['old'] = {'subnet': None}
ret['changes']['new'] = _describe
ret['comment'] = 'Subnet {0} created.'.format(name)
return ret
ret['comment'] = 'Subnet present.'
else:
ret['comment'] = 'Subnet present.'
if route_table_desc:
if not _describe:
_describe = __salt__['boto_vpc.describe_subnet'](subnet_name=name, region=region,
key=key, keyid=keyid, profile=profile)
if not _verify_subnet_association(route_table_desc, _describe['subnet']['id']):
if __opts__['test']:
msg = 'Subnet is set to be associated with route table {0}'.format(rtid)
ret['comment'] = ' '.join([ret['comment'], msg])
ret['result'] = None
return ret
if 'explicit_route_table_association_id' in _describe['subnet']:
log.debug('Need to disassociate from existing route table')
drt_ret = __salt__['boto_vpc.disassociate_route_table'](_describe['subnet']['explicit_route_table_association_id'],
region=region, key=key, keyid=keyid, profile=profile)
if not drt_ret['disassociated']:
msg = 'Unable to disassociate subnet {0} with its current route table.'.format(name)
ret['comment'] = ' '.join([ret['comment'], msg])
ret['result'] = False
return ret
if 'old' not in ret['changes']:
ret['changes']['old'] = _describe
art_ret = __salt__['boto_vpc.associate_route_table'](route_table_id=route_table_desc['id'],
subnet_name=name, region=region,
key=key, keyid=keyid, profile=profile)
if 'error' in art_ret:
msg = 'Failed to associate subnet {0} with route table {1}: {2}.'.format(name, rtid,
art_ret['error']['message'])
ret['comment'] = ' '.join([ret['comment'], msg])
ret['result'] = False
return ret
else:
msg = 'Subnet successfully associated with route table {0}.'.format(rtid)
ret['comment'] = ' '.join([ret['comment'], msg])
if 'new' not in ret['changes']:
ret['changes']['new'] = __salt__['boto_vpc.describe_subnet'](subnet_name=name, region=region,
key=key, keyid=keyid, profile=profile)
else:
ret['changes']['new']['subnet']['explicit_route_table_association_id'] = art_ret['association_id']
else:
ret['comment'] = ' '.join([ret['comment'],
'Subnet is already associated with route table {0}'.format(rtid)])
return ret
def _verify_subnet_association(route_table_desc, subnet_id):
'''
Helper function verify a subnet's route table association
route_table_desc
the description of a route table, as returned from boto_vpc.describe_route_table
subnet_id
the subnet id to verify
.. versionadded:: Carbon
'''
if route_table_desc:
if 'associations' in route_table_desc:
for association in route_table_desc['associations']:
if association['subnet_id'] == subnet_id:
return True
return False
def subnet_absent(name=None, subnet_id=None, region=None, key=None, keyid=None, profile=None):
'''
Ensure subnet with passed properties is absent.