From ff0e4fa2b9a6298e5d17a58ae3e4f5f773dcfead Mon Sep 17 00:00:00 2001 From: Stephen Woodrow Date: Tue, 24 Nov 2015 16:40:44 -0800 Subject: [PATCH] Allow boto_elb to manage ELB backend policies. --- salt/modules/boto_elb.py | 32 +++++++++++++++++++ salt/states/boto_elb.py | 68 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/salt/modules/boto_elb.py b/salt/modules/boto_elb.py index 2f1059385b..ec05cf411d 100644 --- a/salt/modules/boto_elb.py +++ b/salt/modules/boto_elb.py @@ -144,6 +144,13 @@ def get_elb_config(name, region=None, key=None, keyid=None, profile=None): listener_dict['certificate'] = _listener.ssl_certificate_id listeners.append(listener_dict) ret['listeners'] = listeners + backends = [] + for _backend in lb.backends: + bs_dict = {} + bs_dict['instance_port'] = _backend.instance_port + bs_dict['policies'] = [p.policy_name for p in _backend.policies] + backends.append(bs_dict) + ret['backends'] = backends ret['subnets'] = lb.subnets ret['security_groups'] = lb.security_groups ret['scheme'] = lb.scheme @@ -828,6 +835,31 @@ def set_listener_policy(name, port, policies=None, region=None, key=None, return True +def set_backend_policy(name, port, policies=None, region=None, key=None, + keyid=None, profile=None): + ''' + Set the policies of an ELB backend server. + + CLI example: + + salt myminion boto_elb.set_backend_policy myelb 443 "[policy1,policy2]" + ''' + conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) + + if not exists(name, region, key, keyid, profile): + return True + if policies is None: + policies = [] + try: + conn.set_lb_policies_of_backend_server(name, port, policies) + log.info('Set policies {0} on ELB {1} backend server {2}'.format(policies, name, port)) + except boto.exception.BotoServerError as e: + log.debug(e) + log.info('Failed to set policy {0} on ELB {1} backend server {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 diff --git a/salt/states/boto_elb.py b/salt/states/boto_elb.py index 6f2e56aaaf..c9b66fb22e 100644 --- a/salt/states/boto_elb.py +++ b/salt/states/boto_elb.py @@ -57,6 +57,10 @@ passed in as a dict, or as a string to pull from pillars or minion config: - elb_port: 8210 instance_port: 8210 elb_protocol: TCP + - backends: + - instance_port: 80 + policies: + - enable-proxy-protocol - health_check: target: 'HTTP:80/' - attributes: @@ -84,6 +88,10 @@ passed in as a dict, or as a string to pull from pillars or minion config: - policy_name: cookie-policy policy_type: LBCookieStickinessPolicyType policy: {} # no policy means this is a session cookie + - policy_name: enable-proxy-protocol + policy_type: ProxyProtocolPolicyType + policy: + ProxyProtocol: true # Using a profile from pillars Ensure myelb ELB exists: @@ -256,6 +264,7 @@ def present( alarms_from_pillar="boto_elb_alarms", policies=None, policies_from_pillar="boto_elb_policies", + backends=None, region=None, key=None, keyid=None, @@ -406,8 +415,8 @@ def present( ret['result'] = _ret['result'] if ret['result'] is False: return ret - _ret = _policies_present(name, policies, policies_from_pillar, listeners, region, key, - keyid, profile) + _ret = _policies_present(name, policies, policies_from_pillar, listeners, + backends, region, key, keyid, profile) ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) if not _ret['result']: @@ -1023,6 +1032,7 @@ def _policies_present( policies, policies_from_pillar, listeners, + backends, region, key, keyid, @@ -1032,6 +1042,8 @@ def _policies_present( policies = [] pillar_policies = __salt__['config.option'](policies_from_pillar, []) policies = policies + pillar_policies + if backends is None: + backends = [] # check for policy name uniqueness and correct type policy_names = set() @@ -1058,6 +1070,14 @@ def _policies_present( raise SaltInvocationError('Listener {0} on ELB {1} refers to ' 'undefined policy {2}.'.format(l['elb_port'], name, p)) + # check that backends refer to valid policy names + for b in backends: + for p in b.get('policies', []): + if p not in policy_names: + raise SaltInvocationError('Backend {0} on ELB {1} refers to ' + 'undefined policy ' + '{2}.'.format(b['instance_port'], name, p)) + ret = {'result': True, 'comment': '', 'changes': {}} lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) @@ -1107,6 +1127,17 @@ def _policies_present( if re.match(r'^ELBSecurityPolicy-\d{4}-\d{2}$', p): default_aws_policies.add(p) + expected_policies_by_backend = {} + for b in backends: + expected_policies_by_backend[b['instance_port']] = set( + [cnames_by_name[p] for p in b.get('policies', [])]) + + actual_policies_by_backend = {} + for b in lb['backends']: + backend_policies = set(b.get('policies', [])) + actual_policies_by_backend[b['instance_port']] = backend_policies + + to_delete = [] to_create = [] @@ -1126,6 +1157,14 @@ def _policies_present( if policies != expected_policies_by_listener.get(port, set()): listeners_to_update.add(port) + backends_to_update = set() + for port, policies in expected_policies_by_backend.iteritems(): + if policies != actual_policies_by_backend.get(port, set()): + backends_to_update.add(port) + for port, policies in actual_policies_by_backend.iteritems(): + if policies != expected_policies_by_backend.get(port, set()): + backends_to_update.add(port) + if __opts__['test']: msg = [] if to_create or to_delete: @@ -1136,6 +1175,8 @@ def _policies_present( msg.append('Policy {0} deleted.'.format(policy)) for listener in listeners_to_update: msg.append('Listener {0} policies updated.'.format(listener)) + for backend in backends_to_update: + msg.append('Backend {0} policies updated.'.format(backend)) else: msg.append('Policies already set on ELB {0}.'.format(name)) ret['comment'] = ' '.join(msg) @@ -1186,6 +1227,29 @@ def _policies_present( ret['result'] = False return ret + for port in backends_to_update: + policy_set = __salt__['boto_elb.set_backend_policy']( + name=name, + port=port, + policies=list(expected_policies_by_backend.get(port, [])), + region=region, + key=key, + keyid=keyid, + profile=profile) + if policy_set: + policy_key = 'backend_{0}_policy'.format(port) + ret['changes'][policy_key] = { + 'old': list(actual_policies_by_backend.get(port, [])), + 'new': list(expected_policies_by_backend.get(port, [])), + } + comment = "Policy {0} was created on ELB {1} backend {2}".format( + expected_policies_by_backend[port], name, port) + ret['comment'] = ' '.join([ret['comment'], comment]) + ret['result'] = True + else: + ret['result'] = False + return ret + if to_delete: for policy_name in to_delete: deleted = __salt__['boto_elb.delete_policy'](