Merging tag and cname modifications

This commit is contained in:
Ethan Moore 2015-10-06 17:01:33 +00:00
parent 2f715803b2
commit 3316959866
3 changed files with 362 additions and 21 deletions

View file

@ -77,6 +77,7 @@ except ImportError:
# Import Salt libs
from salt.ext.six import string_types
from salt.utils.boto_elb_tag import TagDescriptions as TagDescriptions
import salt.utils.odict as odict
@ -144,6 +145,7 @@ def get_elb_config(name, region=None, key=None, keyid=None, profile=None):
ret['security_groups'] = lb.security_groups
ret['scheme'] = lb.scheme
ret['dns_name'] = lb.dns_name
ret['tags'] = _get_all_tags(conn, name)
lb_policy_lists = [
lb.policies.app_cookie_stickiness_policies,
lb.policies.lb_cookie_stickiness_policies,
@ -743,3 +745,130 @@ def set_listener_policy(name, port, policies=None, region=None, key=None,
log.info('Failed to set policy {0} on ELB {1} listener {2}: {3}'.format(policies, name, port, e.message))
return False
return True
def set_tags(name, tags, region=None, key=None, keyid=None, profile=None):
'''
Add the tags on an ELB
.. versionadded:: Boron
name
name of the ELB
tags
dict of name/value pair tags
CLI Example:
.. code-block:: bash
salt myminion boto_elb.set_tags my-elb-name "{'Tag1': 'Value', 'Tag2': 'Another Value'}"
'''
if exists(name, region, key, keyid, profile):
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
ret = _add_tags(conn, name, tags)
return ret
else:
return False
def delete_tags(name, tags, region=None, key=None, keyid=None, profile=None):
'''
Add the tags on an ELB
name
name of the ELB
tags
list of tags to remove
CLI Example:
.. code-block:: bash
salt myminion boto_elb.delete_tags my-elb-name ['TagToRemove1', 'TagToRemove2']
'''
if exists(name, region, key, keyid, profile):
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
ret = _remove_tags(conn, name, tags)
return ret
else:
return False
def _build_tag_param_list(params, tags):
'''
helper function to build a tag parameter list to send
'''
keys = sorted(tags.keys())
i = 1
for key in keys:
value = tags[key]
params['Tags.member.{0}.Key'.format(i)] = key
if value is not None:
params['Tags.member.{0}.Value'.format(i)] = value
i += 1
def _get_all_tags(conn, load_balancer_names=None):
'''
Retrieve all the metadata tags associated with your ELB(s).
:type load_balancer_names: list
:param load_balancer_names: An optional list of load balancer names.
:rtype: list
:return: A list of :class:`boto.ec2.elb.tag.Tag` objects
'''
params = {}
if load_balancer_names:
conn.build_list_params(params, load_balancer_names,
'LoadBalancerNames.member.%d')
tags = conn.get_object('DescribeTags', params, TagDescriptions,
verb='POST')
if tags[load_balancer_names]:
return tags[load_balancer_names]
else:
return None
def _add_tags(conn, load_balancer_names, tags):
'''
Create new metadata tags for the specified resource ids.
:type load_balancer_names: list
:param load_balancer_names: A list of load balancer names.
:type tags: dict
:param tags: A dictionary containing the name/value pairs.
If you want to create only a tag name, the
value for that tag should be the empty string
(e.g. '').
'''
params = {}
conn.build_list_params(params, load_balancer_names,
'LoadBalancerNames.member.%d')
_build_tag_param_list(params, tags)
return conn.get_status('AddTags', params, verb='POST')
def _remove_tags(conn, load_balancer_names, tags):
'''
Delete metadata tags for the specified resource ids.
:type load_balancer_names: list
:param load_balancer_names: A list of load balancer names.
:type tags: list
:param tags: A list containing just tag names for the tags to be
deleted.
'''
params = {}
conn.build_list_params(params, load_balancer_names,
'LoadBalancerNames.member.%d')
conn.build_list_params(params, tags,
'Tags.member.%d.Key')
return conn.get_status('RemoveTags', params, verb='POST')

View file

@ -206,6 +206,21 @@ Overriding the alarm values on the resource:
UnHealthyHostCount:
attributes:
threshold: 2.0
Tags can also be set:
.. versionadded:: Boron
.. code-block:: yaml
Ensure myelb ELB exists:
boto_elb.present:
- name: myelb
- region: us-east-1
- profile: myelbprofile
- tags:
MyTag: 'My Tag Value'
OtherTag: 'My Other Value'
'''
# Import Python Libs
@ -218,6 +233,7 @@ import salt.utils.dictupdate as dictupdate
from salt.exceptions import SaltInvocationError
import salt.ext.six as six
def __virtual__():
'''
Only load if boto is available.
@ -244,7 +260,8 @@ def present(
key=None,
keyid=None,
profile=None,
wait_for_sync=True):
wait_for_sync=True,
tags=None):
'''
Ensure the ELB exists.
@ -282,8 +299,11 @@ def present(
state will override those from pillar.
cnames
A list of cname dicts with attributes: name, zone, ttl, and identifier.
A list of cname dicts with attributes needed for the DNS add_record state.
By default the boto_route53.add_record state will be used, which requires: name, zone, ttl, and identifier.
See the boto_route53 state for information about these attributes.
Other DNS modules can be called by specifying the provider keyword.
the cnames dict will be passed to the state as kwargs.
alarms:
a dictionary of name->boto_cloudwatch_alarm sections to be associated with this ELB.
@ -310,6 +330,9 @@ def present(
wait_for_sync
Wait for an INSYNC change status from Route53.
tags
dict of tags
'''
# load data from attributes_from_pillar and merge with attributes
@ -352,18 +375,22 @@ def present(
name, region, key, keyid, profile
)
for cname in cnames:
_ret = __states__['boto_route53.present'](
name=cname.get('name'),
value=lb['dns_name'],
zone=cname.get('zone'),
record_type='CNAME',
identifier=cname.get('identifier', None),
ttl=cname.get('ttl', None),
region=region,
key=key,
keyid=keyid,
profile=profile
)
_ret = None
dns_provider = 'boto_route53'
cname['record_type'] = 'CNAME'
cname['value'] = lb['dns_name']
if 'provider' in cname:
dns_provider = cname.pop('provider')
if dns_provider == 'boto_route53':
if 'profile' not in cname:
cname['profile'] = profile
if 'key' not in cname:
cname['key'] = key
if 'keyid' not in cname:
cname['keyid'] = keyid
if 'region' not in cname:
cname['region'] = region
_ret = __states__['.'.join([dns_provider, 'present'])](**cname)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
@ -378,7 +405,14 @@ def present(
if ret['result'] is False:
return ret
_ret = _policies_present(name, policies, policies_from_pillar, listeners, region, key,
keyid, profile)
keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _tags_present(name, tags, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
@ -452,6 +486,7 @@ def register_instances(name, instances, region=None, key=None, keyid=None,
DEFAULT_PILLAR_LISTENER_POLICY_KEY = 'boto_elb_listener_policies'
def _elb_present(
name,
availability_zones,
@ -506,6 +541,7 @@ def _elb_present(
vpc_id = __salt__['boto_vpc.get_subnet_association'](
subnets, region, key, keyid, profile
)
vpc_id = vpc_id.get('vpc_id')
if not vpc_id:
msg = 'Subnets {0} do not map to a valid vpc id.'.format(subnets)
raise SaltInvocationError(msg)
@ -999,17 +1035,17 @@ def _policies_present(
for p in policies:
if 'policy_name' not in p:
raise SaltInvocationError('policy_name is a required value for '
'policies.')
'policies.')
if 'policy_type' not in p:
raise SaltInvocationError('policy_type is a required value for '
'policies.')
'policies.')
if 'policy' not in p:
raise SaltInvocationError('policy is a required value for '
'listeners.')
'listeners.')
# check for unique policy names
if p['policy_name'] in policy_names:
raise SaltInvocationError('Policy names must be unique: policy {}'
' is declared twice.'.format(p['policy_name']))
' is declared twice.'.format(p['policy_name']))
policy_names.add(p['policy_name'])
# check that listeners refer to valid policy names
@ -1017,7 +1053,7 @@ def _policies_present(
for p in l.get('policies', []):
if p not in policy_names:
raise SaltInvocationError('Listener {} on ELB {} refers to '
'undefined policy {}.'.format(l['elb_port'], name, p))
'undefined policy {}.'.format(l['elb_port'], name, p))
ret = {'result': True, 'comment': '', 'changes': {}}
@ -1145,7 +1181,7 @@ def _policies_present(
if to_delete:
for policy_name in to_delete:
deleted = __salt__['boto_elb.delete_policy'](
deleted = __salt__['boto_elb.delete_policy'](
name=name,
policy_name=policy_name,
region=region,
@ -1181,6 +1217,12 @@ def absent(
key=None,
keyid=None,
profile=None):
'''
Ensure an ELB does not exist
name
name of the ELB
'''
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile)
@ -1201,3 +1243,95 @@ def absent(
else:
ret['comment'] = '{0} ELB does not exist.'.format(name)
return ret
def _tags_present(name,
tags,
region,
key,
keyid,
profile):
'''
helper function to validate tags on elb
'''
ret = {'result': True, 'comment': '', 'changes': {}}
if tags:
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
tags_to_add = tags
tags_to_update = {}
tags_to_remove = []
if lb['tags']:
for _tag in lb['tags'].keys():
if _tag not in tags.keys():
if _tag not in tags_to_remove:
tags_to_remove.append(_tag)
else:
if tags[_tag] != lb['tags'][_tag]:
tags_to_update[_tag] = tags[_tag]
tags_to_add.pop(_tag)
if tags_to_remove:
if __opts__['test']:
msg = 'The following tag{0} set to be removed: {1}.'.format(
('s are' if len(tags_to_remove) > 1 else ' is'), ', '.join(tags_to_remove))
ret['comment'] = ' '.join([ret['comment'], msg])
ret['result'] = None
else:
_ret = __salt__['boto_elb.delete_tags'](
name,
tags_to_remove,
region,
key,
keyid,
profile)
if not _ret:
ret['result'] = False
msg = 'Error attempting to delete tag {0}.'.format(tags_to_remove)
ret['comment'] = ' '.join([ret['comment'], msg])
return ret
if 'old' not in ret['changes']:
ret['changes'] = dictupdate.update(ret['changes'], {'old': {'tags': {}}})
for _tag in tags_to_remove:
ret['changes']['old']['tags'][_tag] = lb['tags'][_tag]
if tags_to_add or tags_to_update:
if __opts__['test']:
if tags_to_add:
msg = 'The following tag{0} set to be added: {1}.'.format(
('s are' if len(tags_to_add.keys()) > 1 else ' is'),
', '.join(tags_to_add.keys()))
ret['comment'] = ' '. join([ret['comment'], msg])
ret['result'] = None
if tags_to_update:
msg = 'The following tag {0} set to be updated: {1}.'.format(
('values are' if len(tags_to_update.keys()) > 1 else 'value is'),
', '.join(tags_to_update.keys()))
ret['comment'] = ' '.join([ret['comment'], msg])
ret['result'] = None
else:
all_tag_changes = dictupdate.update(tags_to_add, tags_to_update)
_ret = __salt__['boto_elb.set_tags'](
name,
all_tag_changes,
region,
key,
keyid,
profile)
if not _ret:
ret['result'] = False
msg = 'Error attempting to set tags.'
ret['comment'] = ' '.join([ret['comment'], msg])
return ret
if 'old' not in ret['changes']:
ret['changes'] = dictupdate.update(ret['changes'], {'old': {'tags': {}}})
if 'new' not in ret['changes']:
ret['changes'] = dictupdate.update(ret['changes'], {'new': {'tags': {}}})
for tag in all_tag_changes:
ret['changes']['new']['tags'][tag] = tags[tag]
if 'tags' in lb:
if lb['tags']:
if tag in lb['tags']:
ret['changes']['old']['tags'][tag] = lb['tags'][tag]
if not tags_to_update and not tags_to_remove and not tags_to_add:
msg = 'Tags are already set.'
ret['comment'] = ' '.join([ret['comment'], msg])
return ret

View file

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2010 Mitch Garnaat http://garnaat.org/
# Copyright (c) 2010, Eucalyptus Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
class TagDescriptions(dict):
'''
A TagDescriptions is used to collect the tags associated with ELB resources.
See :class:`boto.ec2.elb.LoadBalancer` for more details.
'''
def __init__(self, connection=None):
dict.__init__(self)
self.connection = connection
self._load_balancer_name = None
self._tags = None
def startElement(self, name, attrs, connection):
if name == 'member':
self.load_balancer_name = None
self.tags = None
if name == 'Tags':
self._tags = TagSet()
return self._tags
return None
def endElement(self, name, value, connection):
if name == 'LoadBalancerName':
self._load_balancer_name = value
elif name == 'member':
self[self._load_balancer_name] = self._tags
class TagSet(dict):
'''
A TagSet is used to collect the tags associated with a particular
ELB resource. See :class:`boto.ec2.elb.LoadBalancer` for more
details.
'''
def __init__(self, connection=None):
dict.__init__(self)
self.connection = connection
self._current_key = None
self._current_value = None
def startElement(self, name, attrs, connection):
if name == 'member':
self._current_key = None
self._current_value = None
return None
def endElement(self, name, value, connection):
if name == 'Key':
self._current_key = value
elif name == 'Value':
self._current_value = value
elif name == 'member':
self[self._current_key] = self._current_value