mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Initial commit of boto_elb module and state.
This commit adds execution module and state module support for AWS ELBs. The state module provides support for ensuring ELBs are absent or present. If present the state will ensure health checks, listeners, availability zones, attributes, and other ELB properties. The execution module adds support for all the necessary actions in the state.
This commit is contained in:
parent
2e2a266622
commit
804e67cf8f
2 changed files with 1084 additions and 0 deletions
531
salt/modules/boto_elb.py
Normal file
531
salt/modules/boto_elb.py
Normal file
|
@ -0,0 +1,531 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Connection module for Amazon ELB
|
||||
|
||||
.. versionadded:: Helium
|
||||
|
||||
:configuration: This module accepts explicit elb credentials but can also utilize
|
||||
IAM roles assigned to the instance trough Instance Profiles. Dynamic
|
||||
credentials are then automatically obtained from AWS API and no further
|
||||
configuration is necessary. More Information available at::
|
||||
|
||||
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
|
||||
|
||||
If IAM roles are not used you need to specify them either in a pillar or
|
||||
in the minion's config file::
|
||||
|
||||
elb.keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
elb.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
|
||||
A region may also be specified in the configuration::
|
||||
|
||||
elb.region: us-east-1
|
||||
|
||||
If a region is not specified, the default is us-east-1.
|
||||
|
||||
It's also possible to specify key, keyid and region via a profile, either
|
||||
as a passed in dict, or as a string to pull from pillars or minion config:
|
||||
|
||||
myprofile:
|
||||
keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
region: us-east-1
|
||||
|
||||
:depends: boto
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
import logging
|
||||
import json
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Import third party libs
|
||||
try:
|
||||
import boto
|
||||
import boto.ec2
|
||||
import boto.ec2.elb
|
||||
from boto.ec2.elb import HealthCheck
|
||||
from boto.ec2.elb.attributes import AccessLogAttribute
|
||||
from boto.ec2.elb.attributes import CrossZoneLoadBalancingAttribute
|
||||
logging.getLogger('boto').setLevel(logging.CRITICAL)
|
||||
HAS_BOTO = True
|
||||
except ImportError:
|
||||
HAS_BOTO = False
|
||||
|
||||
from salt._compat import string_types
|
||||
import salt.utils.odict as odict
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load if boto libraries exist.
|
||||
'''
|
||||
if not HAS_BOTO:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def exists(name, region=None, key=None, keyid=None, profile=None):
|
||||
'''
|
||||
Check to see if an ELB exists.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.exists myelb region=us-east-1
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
try:
|
||||
conn.get_all_load_balancers(load_balancer_names=[name])
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
return False
|
||||
|
||||
|
||||
def get_elb_config(name, region=None, key=None, keyid=None, profile=None):
|
||||
'''
|
||||
Check to see if an ELB exists.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.exists myelb region=us-east-1
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return []
|
||||
try:
|
||||
lb = conn.get_all_load_balancers(load_balancer_names=[name])
|
||||
lb = lb[0]
|
||||
ret = {}
|
||||
ret['availability_zones'] = lb.availability_zones
|
||||
listeners = []
|
||||
for _listener in lb.listeners:
|
||||
# Making this a list makes our life easier and is also the only way
|
||||
# to include the certificate.
|
||||
complex_listener = list(_listener.get_complex_tuple())
|
||||
# boto, you're *killing* me with this. boto doesn't include the
|
||||
# certificate when calling get_complex_tuple, so you need to also
|
||||
# get the certificate. So. Much. Hate.
|
||||
if _listener.ssl_certificate_id:
|
||||
complex_listener.append(_listener.ssl_certificate_id)
|
||||
listeners.append(complex_listener)
|
||||
ret['listeners'] = listeners
|
||||
ret['subnets'] = lb.subnets
|
||||
ret['security_groups'] = lb.security_groups
|
||||
ret['scheme'] = lb.scheme
|
||||
return ret
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
return []
|
||||
|
||||
|
||||
def create(name, availability_zones, listeners=None, subnets=None,
|
||||
security_groups=None, scheme='internet-facing',
|
||||
region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Create an ELB
|
||||
|
||||
CLI example to create an ELB::
|
||||
|
||||
salt myminion boto_elb.create myelb '["us-east-1a", "us-east-1e"]' listeners='[["HTTPS", "HTTP", 443, 80, "arn:aws:iam::1111111:server-certificate/mycert"]]' region=us-east-1
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if __salt__['boto_elb.exists'](name, region, key, keyid, profile):
|
||||
return True
|
||||
if isinstance(availability_zones, string_types):
|
||||
availability_zones = json.loads(availability_zones)
|
||||
if isinstance(listeners, string_types):
|
||||
listeners = json.loads(listeners)
|
||||
# Combining listeners and complex_listeners together makes our lives
|
||||
# easier in some ways, especially since during introspection you can
|
||||
# only get a combined set of listeners back from boto; however, boto
|
||||
# requires us to send in separate listeners and complex listeners and
|
||||
# the only real difference is the size. It feels like amazon/boto hate
|
||||
# developers and wish to make us suffer.
|
||||
_listeners = []
|
||||
_complex_listeners = []
|
||||
for listener in listeners:
|
||||
if len(listener) <= 3:
|
||||
_listeners.append(listener)
|
||||
else:
|
||||
_complex_listeners.append(listener)
|
||||
try:
|
||||
lb = conn.create_load_balancer(name, availability_zones, _listeners,
|
||||
subnets, security_groups, scheme,
|
||||
_complex_listeners)
|
||||
if lb:
|
||||
log.info('Created ELB {0}'.format(name))
|
||||
return True
|
||||
else:
|
||||
msg = 'Failed to create ELB {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to create ELB {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def delete(name, region=None, key=None, keyid=None, profile=None):
|
||||
'''
|
||||
Delete an ELB.
|
||||
|
||||
CLI example to delete an ELB::
|
||||
|
||||
salt myminion boto_elb.delete myelb region=us-east-1
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if not __salt__['boto_elb.exists'](name, region, key, keyid, profile):
|
||||
return True
|
||||
try:
|
||||
conn.delete_load_balancer(name)
|
||||
msg = 'Deleted ELB {0}.'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to delete ELB {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def create_listeners(name, listeners=None, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Create listeners on an ELB.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.create_listeners myelb listeners='[["HTTPS", "HTTP", 443, 80, "arn:aws:iam::11 11111:server-certificate/mycert"]]'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(listeners, string_types):
|
||||
listeners = json.loads(listeners)
|
||||
# Combining listeners and complex_listeners together makes our lives
|
||||
# easier in some ways, especially since during introspection you can
|
||||
# only get a combined set of listeners back from boto; however, boto
|
||||
# requires us to send in separate listeners and complex listeners and
|
||||
# the only real difference is the size. It feels like amazon/boto hate
|
||||
# developers and wish to make us suffer.
|
||||
_listeners = []
|
||||
_complex_listeners = []
|
||||
for listener in listeners:
|
||||
if len(listener) <= 3:
|
||||
_listeners.append(listener)
|
||||
else:
|
||||
_complex_listeners.append(listener)
|
||||
try:
|
||||
conn.create_load_balancer_listeners(name, _listeners,
|
||||
_complex_listeners)
|
||||
msg = 'Created ELB listeners on {0}'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to create ELB listeners on {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def delete_listeners(name, ports, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Delete listeners on an ELB.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.delete_listeners myelb '[80,443]'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(ports, string_types):
|
||||
ports = json.loads(ports)
|
||||
try:
|
||||
conn.delete_load_balancer_listeners(name, ports)
|
||||
msg = 'Deleted ELB listeners on {0}'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to delete ELB listeners on {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def enable_availability_zones(name, availability_zones, region=None, key=None,
|
||||
keyid=None, profile=None):
|
||||
'''
|
||||
Enable availability zones for ELB.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.enable_availability_zones myelb '["us-east-1a"]'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(availability_zones, string_types):
|
||||
availability_zones = json.loads(availability_zones)
|
||||
try:
|
||||
conn.enable_availability_zones(name, availability_zones)
|
||||
msg = 'Enabled availability_zones on ELB {0}'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to enable availability_zones on ELB {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def disable_availability_zones(name, availability_zones, region=None, key=None,
|
||||
keyid=None, profile=None):
|
||||
'''
|
||||
Disable availability zones for ELB.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.disable_availability_zones myelb '["us-east-1a"]'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(availability_zones, string_types):
|
||||
availability_zones = json.loads(availability_zones)
|
||||
try:
|
||||
conn.disable_availability_zones(name, availability_zones)
|
||||
msg = 'Disabled availability_zones on ELB {0}'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to disable availability_zones on ELB {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def attach_subnets(name, subnets, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Attach ELB to subnets.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.attach_subnets myelb '["mysubnet"]'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(subnets, string_types):
|
||||
subnets = json.loads(subnets)
|
||||
try:
|
||||
conn.attach_lb_to_subnets(name, subnets)
|
||||
msg = 'Attached ELB {0} on subnets.'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to attach ELB {0} on subnets.'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def detach_subnets(name, subnets, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Detach ELB from subnets.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.detach_subnets myelb '["mysubnet"]'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(subnets, string_types):
|
||||
subnets = json.loads(subnets)
|
||||
try:
|
||||
conn.detach_lb_from_subnets(name, subnets)
|
||||
msg = 'Detatched ELB {0} from subnets.'.format(name)
|
||||
log.info(msg)
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to detach ELB {0} from subnets.'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def get_attributes(name, region=None, key=None, keyid=None, profile=None):
|
||||
'''
|
||||
Check to see if attributes are set on an ELB.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.get_attributes myelb
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return {}
|
||||
try:
|
||||
lbattrs = conn.get_all_lb_attributes(name)
|
||||
ret = odict.OrderedDict()
|
||||
ret['access_log'] = odict.OrderedDict()
|
||||
ret['cross_zone_load_balancing'] = odict.OrderedDict()
|
||||
al = lbattrs.access_log
|
||||
czlb = lbattrs.cross_zone_load_balancing
|
||||
ret['access_log']['enabled'] = al.enabled
|
||||
ret['access_log']['s3_bucket_name'] = al.s3_bucket_name
|
||||
ret['access_log']['s3_bucket_prefix'] = al.s3_bucket_prefix
|
||||
ret['access_log']['emit_interval'] = al.emit_interval
|
||||
ret['cross_zone_load_balancing']['enabled'] = czlb.enabled
|
||||
return ret
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
log.error('ELB {0} does not exist.'.format(name))
|
||||
return {}
|
||||
|
||||
|
||||
def set_attributes(name, attributes, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Set attributes on an ELB.
|
||||
|
||||
CLI example to set attributes on an ELB::
|
||||
|
||||
salt myminion boto_elb.set_attributes myelb '{"access_log": {"enabled": "true", "s3_bucket_name": "mybucket", "s3_bucket_prefix": "mylogs/", "emit_interval": "5"}}' region=us-east-1
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
al = attributes.get('access_log', {})
|
||||
czlb = attributes.get('cross_zone_load_balancing', {})
|
||||
if not al and not czlb:
|
||||
log.error('No supported attributes for ELB.')
|
||||
return False
|
||||
if al:
|
||||
_al = AccessLogAttribute()
|
||||
_al.enabled = al.get('enabled', False)
|
||||
if not _al.enabled:
|
||||
msg = 'Access log attribute configured, but enabled config missing'
|
||||
log.error(msg)
|
||||
return False
|
||||
_al.s3_bucket_name = al.get('s3_bucket_name', None)
|
||||
_al.s3_bucket_prefix = al.get('s3_bucket_prefix', None)
|
||||
_al.emit_interval = al.get('emit_interval', None)
|
||||
added_attr = conn.modify_lb_attribute(name, 'accessLog', _al)
|
||||
if added_attr:
|
||||
log.info('Added access_log attribute to {0} elb.'.format(name))
|
||||
else:
|
||||
msg = 'Failed to add access_log attribute to {0} elb.'
|
||||
log.error(msg.format(name))
|
||||
return False
|
||||
if czlb:
|
||||
_czlb = CrossZoneLoadBalancingAttribute()
|
||||
_czlb.enabled = czlb['enabled']
|
||||
added_attr = conn.modify_lb_attribute(name, 'crossZoneLoadBalancing',
|
||||
_czlb.enabled)
|
||||
if added_attr:
|
||||
msg = 'Added cross_zone_load_balancing attribute to {0} elb.'
|
||||
log.info(msg.format(name))
|
||||
else:
|
||||
log.error('Failed to add cross_zone_load_balancing attribute.')
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_health_check(name, region=None, key=None, keyid=None, profile=None):
|
||||
'''
|
||||
Get the health check configured for this ELB.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_elb.get_health_check myelb
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return {}
|
||||
try:
|
||||
lb = conn.get_all_load_balancers(load_balancer_names=[name])
|
||||
lb = lb[0]
|
||||
ret = odict.OrderedDict()
|
||||
hc = lb.health_check
|
||||
ret['interval'] = hc.interval
|
||||
ret['target'] = hc.target
|
||||
ret['healthy_threshold'] = hc.healthy_threshold
|
||||
ret['timeout'] = hc.timeout
|
||||
ret['unhealthy_threshold'] = hc.unhealthy_threshold
|
||||
return ret
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
log.error('ELB {0} does not exist.'.format(name))
|
||||
return {}
|
||||
|
||||
|
||||
def set_health_check(name, health_check, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Set attributes on an ELB.
|
||||
|
||||
CLI example to set attributes on an ELB::
|
||||
|
||||
salt myminion boto_elb.set_health_check myelb '{"target": "HTTP:80/"}'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
hc = HealthCheck(**health_check)
|
||||
try:
|
||||
conn.configure_health_check(name, hc)
|
||||
log.info('Configured health check on ELB {0}'.format(name))
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
log.info('Failed to configure health check on ELB {0}'.format(name))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _get_conn(region, key, keyid, profile):
|
||||
'''
|
||||
Get a boto connection to ELB.
|
||||
'''
|
||||
if profile:
|
||||
if isinstance(profile, string_types):
|
||||
_profile = __salt__['config.option'](profile)
|
||||
elif isinstance(profile, dict):
|
||||
_profile = profile
|
||||
key = _profile.get('key', None)
|
||||
keyid = _profile.get('keyid', None)
|
||||
region = _profile.get('keyid', None)
|
||||
|
||||
if not region and __salt__['config.option']('elb.region'):
|
||||
region = __salt__['config.option']('elb.region')
|
||||
|
||||
if not region:
|
||||
region = 'us-east-1'
|
||||
|
||||
if not key and __salt__['config.option']('elb.key'):
|
||||
key = __salt__['config.option']('elb.key')
|
||||
if not keyid and __salt__['config.option']('elb.keyid'):
|
||||
keyid = __salt__['config.option']('elb.keyid')
|
||||
|
||||
try:
|
||||
conn = boto.ec2.elb.connect_to_region(region, aws_access_key_id=keyid,
|
||||
aws_secret_access_key=key)
|
||||
except boto.exception.NoAuthHandlerFound:
|
||||
log.error('No authentication credentials found when attempting to'
|
||||
' make boto elb connection.')
|
||||
return None
|
||||
return conn
|
553
salt/states/boto_elb.py
Normal file
553
salt/states/boto_elb.py
Normal file
|
@ -0,0 +1,553 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manage ELBs
|
||||
=================
|
||||
|
||||
.. versionadded:: Helium
|
||||
|
||||
Create and destroy ELBs. Be aware that this interacts with Amazon's
|
||||
services, and so may incur charges.
|
||||
|
||||
This module uses boto, which can be installed via package, or pip.
|
||||
|
||||
This module accepts explicit elb credentials but can also utilize
|
||||
IAM roles assigned to the instance trough Instance Profiles. Dynamic
|
||||
credentials are then automatically obtained from AWS API and no further
|
||||
configuration is necessary. More Information available at::
|
||||
|
||||
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
|
||||
|
||||
If IAM roles are not used you need to specify them either in a pillar or
|
||||
in the minion's config file::
|
||||
|
||||
elb.keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
elb.key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
|
||||
It's also possible to specify key, keyid and region via a profile, either
|
||||
as a passed in dict, or as a string to pull from pillars or minion config:
|
||||
|
||||
myprofile:
|
||||
keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
region: us-east-1
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
myelb:
|
||||
boto_elb.present:
|
||||
- region: us-east-1
|
||||
- keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
- key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
- listeners:
|
||||
- - 443
|
||||
- 80
|
||||
- HTTPS
|
||||
- HTTP
|
||||
- 'arn:aws:iam::1111111:server-certificate/mycert'
|
||||
- - 8210
|
||||
- 8210
|
||||
- TCP
|
||||
- TCP
|
||||
- health_check:
|
||||
target: 'HTTP:80/'
|
||||
- attributes:
|
||||
cross_zone_load_balancing:
|
||||
enabled: true
|
||||
access_log:
|
||||
enabled: true
|
||||
s3_bucket_name: 'mybucket'
|
||||
s3_bucket_prefix: '/my-logs/'
|
||||
emit_interval: 5
|
||||
|
||||
# Using a profile from pillars
|
||||
myelb:
|
||||
boto_elb.present:
|
||||
- region: us-east-1
|
||||
- profile: myelbprofile
|
||||
|
||||
# Passing in a profile
|
||||
myelb:
|
||||
boto_elb.present:
|
||||
- region: us-east-1
|
||||
- profile:
|
||||
keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
'''
|
||||
import salt.utils.dictupdate as dictupdate
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load if boto is available.
|
||||
'''
|
||||
return 'boto_elb' if 'boto_elb.exists' in __salt__ else False
|
||||
|
||||
|
||||
def present(
|
||||
name,
|
||||
availability_zones,
|
||||
listeners,
|
||||
subnets=None,
|
||||
security_groups=None,
|
||||
scheme='internet-facing',
|
||||
health_check=None,
|
||||
attributes=None,
|
||||
region=None,
|
||||
key=None,
|
||||
keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Ensure the IAM role exists.
|
||||
|
||||
name
|
||||
Name of the IAM role.
|
||||
|
||||
availability_zones
|
||||
A list of availability zones for this ELB.
|
||||
|
||||
listeners
|
||||
A list of listener lists; example: [['443', 'HTTPS', 'arn:aws:iam::1111111:server-certificate/mycert'], ['8443', '80', 'HTTPS', 'HTTP', 'arn:aws:iam::1111111:server-certificate/mycert']]
|
||||
|
||||
subnets
|
||||
A list of subnet IDs in your VPC to attach to your LoadBalancer.
|
||||
|
||||
security_groups
|
||||
The security groups assigned to your LoadBalancer within your VPC.
|
||||
|
||||
scheme
|
||||
The type of a LoadBalancer. internet-facing or internal. Once set, can not be modified.
|
||||
|
||||
health_check
|
||||
A dict defining the health check for this ELB.
|
||||
|
||||
attributes
|
||||
A dict defining the attributes to set on this ELB.
|
||||
|
||||
region
|
||||
Region to connect to.
|
||||
|
||||
key
|
||||
Secret key to be used.
|
||||
|
||||
keyid
|
||||
Access key to be used.
|
||||
|
||||
profile
|
||||
A dict with region, key and keyid, or a pillar key (string)
|
||||
that contains a dict with region, key and keyid.
|
||||
'''
|
||||
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
|
||||
_ret = _elb_present(name, availability_zones, listeners, subnets,
|
||||
security_groups, scheme, region, key, keyid, profile)
|
||||
ret['changes'] = _ret['changes']
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if _ret['result'] is not None:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
_ret = _attributes_present(name, attributes, region, key, keyid, profile)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if _ret['result'] is not None:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
_ret = _health_check_present(name, health_check, region, key, keyid,
|
||||
profile)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if _ret['result'] is not None:
|
||||
ret['result'] = _ret['result']
|
||||
return ret
|
||||
|
||||
|
||||
def _elb_present(
|
||||
name,
|
||||
availability_zones,
|
||||
listeners,
|
||||
subnets,
|
||||
security_groups,
|
||||
scheme,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile):
|
||||
ret = {'result': None, 'comment': '', 'changes': {}}
|
||||
if not listeners:
|
||||
listeners = []
|
||||
for listener in listeners:
|
||||
if len(listener) < 3:
|
||||
raise SaltInvocationError('Listeners must have at minimum port,'
|
||||
' instance_port and protocol values in'
|
||||
' the provided list.')
|
||||
elif len(listener) == 3:
|
||||
# Returned listeners will have 4 values. There's no way for us to
|
||||
# reliably get back complex and non-complex listeners, so we'll
|
||||
# have to make due with this.
|
||||
listener.append(listener[2])
|
||||
exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile)
|
||||
if not exists:
|
||||
if __opts__['test']:
|
||||
ret['comment'] = 'ELB {0} is set to be created.'.format(name)
|
||||
return ret
|
||||
created = __salt__['boto_elb.create'](name, availability_zones,
|
||||
listeners, subnets,
|
||||
security_groups, scheme, region,
|
||||
key, keyid, profile)
|
||||
if created:
|
||||
ret['result'] = True
|
||||
ret['changes']['old'] = {'elb': None}
|
||||
ret['changes']['new'] = {'elb': name}
|
||||
ret['comment'] = 'ELB {0} created.'.format(name)
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to create {0} ELB.'.format(name)
|
||||
else:
|
||||
ret['comment'] = 'ELB {0} present.'.format(name)
|
||||
_ret = _listeners_present(name, listeners, region, key, keyid, profile)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if _ret['result'] is not None:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
_ret = _zones_present(name, availability_zones, region, key, keyid,
|
||||
profile)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if _ret['result'] is not None:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
_ret = _subnets_present(name, subnets, region, key, keyid,
|
||||
profile)
|
||||
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if _ret['result'] is not None:
|
||||
ret['result'] = _ret['result']
|
||||
return ret
|
||||
|
||||
|
||||
def _listeners_present(
|
||||
name,
|
||||
listeners,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile):
|
||||
ret = {'result': None, 'comment': '', 'changes': {}}
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
|
||||
if not lb:
|
||||
msg = '{0} ELB configuration could not be retreived.'.format(name)
|
||||
ret['comment'] = msg
|
||||
ret['result'] = False
|
||||
return ret
|
||||
if not listeners:
|
||||
listeners = []
|
||||
to_delete = []
|
||||
to_create = []
|
||||
for listener in listeners:
|
||||
if listener not in lb['listeners']:
|
||||
to_create.append(listener)
|
||||
for listener in lb['listeners']:
|
||||
if listener not in listeners:
|
||||
to_delete.append(listener[0])
|
||||
if to_create or to_delete:
|
||||
if __opts__['test']:
|
||||
msg = 'ELB {0} set to have listeners modified.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
if to_delete:
|
||||
deleted = __salt__['boto_elb.delete_listeners'](name, to_delete,
|
||||
region, key, keyid,
|
||||
profile)
|
||||
if deleted:
|
||||
ret['comment'] = 'Deleted listeners on {0} ELB.'.format(name)
|
||||
ret['result'] = True
|
||||
else:
|
||||
msg = 'Failed to delete listeners on {0} ELB.'.format(name)
|
||||
ret['comment'] = msg
|
||||
ret['result'] = False
|
||||
if to_create:
|
||||
created = __salt__['boto_elb.create_listeners'](name, to_create,
|
||||
region, key, keyid,
|
||||
profile)
|
||||
if created:
|
||||
msg = 'Created listeners on {0} ELB.'
|
||||
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
|
||||
if ret['result'] is not False:
|
||||
ret['result'] = True
|
||||
else:
|
||||
msg = 'Failed to create listeners on {0} ELB.'
|
||||
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
|
||||
ret['result'] = False
|
||||
ret['changes']['old'] = {'listeners': lb['listeners']}
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid,
|
||||
profile)
|
||||
ret['changes']['new'] = {'listeners': lb['listeners']}
|
||||
return ret
|
||||
|
||||
|
||||
def _attributes_present(
|
||||
name,
|
||||
attributes,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile):
|
||||
ret = {'result': None, 'comment': '', 'changes': {}}
|
||||
_attributes = __salt__['boto_elb.get_attributes'](name, region, key, keyid,
|
||||
profile)
|
||||
if not _attributes:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to retrieve attributes for ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
attrs_to_set = []
|
||||
if 'cross_zone_load_balancing' in attributes:
|
||||
czlb = attributes['cross_zone_load_balancing']
|
||||
_czlb = _attributes['cross_zone_load_balancing']
|
||||
if czlb['enabled'] != _czlb['enabled']:
|
||||
attrs_to_set.append('cross_zone_load_balancing')
|
||||
if 'access_log' in attributes:
|
||||
for attr, val in attributes['access_log'].iteritems():
|
||||
if str(_attributes['access_log'][attr]) != str(val):
|
||||
attrs_to_set.append('access_log')
|
||||
if 's3_bucket_prefix' in attributes['access_log']:
|
||||
sbp = attributes['access_log']['s3_bucket_prefix']
|
||||
if sbp.startswith('/') or sbp.endswith('/'):
|
||||
raise SaltInvocationError('s3_bucket_prefix can not start or'
|
||||
' end with /.')
|
||||
if attrs_to_set:
|
||||
if __opts__['test']:
|
||||
ret['comment'] = 'ELB {0} set to have attributes set.'.format(name)
|
||||
return ret
|
||||
was_set = __salt__['boto_elb.set_attributes'](name, attributes,
|
||||
region, key, keyid,
|
||||
profile)
|
||||
if was_set:
|
||||
ret['result'] = True
|
||||
ret['changes']['old'] = {'attributes': _attributes}
|
||||
ret['changes']['new'] = {'attributes': attributes}
|
||||
ret['comment'] = 'Set attributes on ELB {0}.'.format(name)
|
||||
else:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to set attributes on ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
else:
|
||||
ret['comment'] = 'Attributes already set on ELB {0}.'.format(name)
|
||||
return ret
|
||||
|
||||
|
||||
def _health_check_present(
|
||||
name,
|
||||
health_check,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile):
|
||||
ret = {'result': None, 'comment': '', 'changes': {}}
|
||||
if not health_check:
|
||||
health_check = {}
|
||||
_health_check = __salt__['boto_elb.get_health_check'](name, region, key,
|
||||
keyid, profile)
|
||||
if not _health_check:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to retrieve health_check for ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
need_to_set = False
|
||||
for attr, val in health_check.iteritems():
|
||||
if str(_health_check[attr]) != str(val):
|
||||
need_to_set = True
|
||||
if need_to_set:
|
||||
if __opts__['test']:
|
||||
msg = 'ELB {0} set to have health check set.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
was_set = __salt__['boto_elb.set_health_check'](name, health_check,
|
||||
region, key, keyid,
|
||||
profile)
|
||||
if was_set:
|
||||
ret['result'] = True
|
||||
ret['changes']['old'] = {'health_check': _health_check}
|
||||
_health_check = __salt__['boto_elb.get_health_check'](name, region,
|
||||
key, keyid,
|
||||
profile)
|
||||
ret['changes']['new'] = {'health_check': _health_check}
|
||||
ret['comment'] = 'Set health check on ELB {0}.'.format(name)
|
||||
else:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to set health check on ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
else:
|
||||
ret['comment'] = 'Health check already set on ELB {0}.'.format(name)
|
||||
return ret
|
||||
|
||||
|
||||
def _zones_present(
|
||||
name,
|
||||
availability_zones,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile):
|
||||
ret = {'result': None, 'comment': '', 'changes': {}}
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
|
||||
if not lb:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to retrieve ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
to_enable = []
|
||||
to_disable = []
|
||||
_zones = lb['availability_zones']
|
||||
for zone in availability_zones:
|
||||
if zone not in _zones:
|
||||
to_enable.append(zone)
|
||||
for zone in _zones:
|
||||
if zone not in availability_zones:
|
||||
to_disable.append(zone)
|
||||
if to_enable or to_disable:
|
||||
if __opts__['test']:
|
||||
msg = 'ELB {0} to have availability zones set.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
if to_enable:
|
||||
enabled = __salt__['boto_elb.enable_availability_zones'](name,
|
||||
to_enable,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile)
|
||||
if enabled:
|
||||
msg = 'Enabled availability zones on {0} ELB.'.format(name)
|
||||
ret['comment'] = msg
|
||||
ret['result'] = True
|
||||
else:
|
||||
msg = 'Failed to enable availability zones on {0} ELB.'
|
||||
ret['comment'] = msg.format(name)
|
||||
ret['result'] = False
|
||||
if to_disable:
|
||||
disabled = __salt__['boto_elb.disable_availability_zones'](name,
|
||||
to_disable,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile)
|
||||
if disabled:
|
||||
msg = 'Disabled availability zones on {0} ELB.'
|
||||
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
|
||||
ret['result'] = True
|
||||
else:
|
||||
msg = 'Failed to disable availability zones on {0} ELB.'
|
||||
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
|
||||
ret['result'] = False
|
||||
ret['changes']['old'] = {'availability_zones':
|
||||
lb['availability_zones']}
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid,
|
||||
profile)
|
||||
ret['changes']['new'] = {'availability_zones':
|
||||
lb['availability_zones']}
|
||||
else:
|
||||
msg = 'Availability zones already set on ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
|
||||
|
||||
def _subnets_present(
|
||||
name,
|
||||
subnets,
|
||||
region,
|
||||
key,
|
||||
keyid,
|
||||
profile):
|
||||
ret = {'result': None, 'comment': '', 'changes': {}}
|
||||
if not subnets:
|
||||
subnets = []
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
|
||||
if not lb:
|
||||
if not __opts__['test']:
|
||||
ret['result'] = False
|
||||
msg = 'Failed to retrieve ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
to_enable = []
|
||||
to_disable = []
|
||||
_subnets = lb['subnets']
|
||||
for subnet in subnets:
|
||||
if subnet not in _subnets:
|
||||
to_enable.append(subnet)
|
||||
for subnet in _subnets:
|
||||
if subnet not in subnets:
|
||||
to_disable.append(subnet)
|
||||
if to_enable or to_disable:
|
||||
if __opts__['test']:
|
||||
msg = 'ELB {0} to have subnets set.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
if to_enable:
|
||||
attached = __salt__['boto_elb.attach_subnets'](name, to_enable,
|
||||
region, key, keyid,
|
||||
profile)
|
||||
if attached:
|
||||
msg = 'Attached subnets on {0} ELB.'.format(name)
|
||||
ret['comment'] = msg
|
||||
ret['result'] = True
|
||||
else:
|
||||
msg = 'Failed to attach subnets on {0} ELB.'
|
||||
ret['comment'] = msg.format(name)
|
||||
ret['result'] = False
|
||||
if to_disable:
|
||||
detached = __salt__['boto_elb.detach_subnets'](name, to_disable,
|
||||
region, key, keyid,
|
||||
profile)
|
||||
if detached:
|
||||
msg = 'Detached subnets on {0} ELB.'
|
||||
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
|
||||
ret['result'] = True
|
||||
else:
|
||||
msg = 'Failed to detach subnets on {0} ELB.'
|
||||
ret['comment'] = ' '.join([ret['comment'], msg.format(name)])
|
||||
ret['result'] = False
|
||||
ret['changes']['old'] = {'subnets': lb['subnets']}
|
||||
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid,
|
||||
profile)
|
||||
ret['changes']['new'] = {'subnets': lb['subnets']}
|
||||
else:
|
||||
msg = 'Subnets already set on ELB {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
|
||||
|
||||
def absent(
|
||||
name,
|
||||
region=None,
|
||||
key=None,
|
||||
keyid=None,
|
||||
profile=None):
|
||||
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
|
||||
|
||||
exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile)
|
||||
if exists:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'ELB {0} is set to be removed.'.format(name)
|
||||
return ret
|
||||
deleted = __salt__['boto_elb.delete'](name, region, key, keyid,
|
||||
profile)
|
||||
if deleted:
|
||||
ret['result'] = True
|
||||
ret['changes']['old'] = {'elb': name}
|
||||
ret['changes']['new'] = {'elb': None}
|
||||
ret['comment'] = 'ELB {0} deleted.'.format(name)
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to delete {0} ELB.'.format(name)
|
||||
else:
|
||||
ret['comment'] = '{0} ELB does not exist.'.format(name)
|
||||
return ret
|
Loading…
Add table
Reference in a new issue