mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 09:23:56 +00:00
Initial commit of boto_lc state module.
This state module handles creating and destroying launch configurations for use with autoscale groups.
This commit is contained in:
parent
259503bb08
commit
91e7f92a22
@ -37,6 +37,8 @@ Connection module for Amazon Autoscale Groups
|
||||
# Import Python libs
|
||||
import logging
|
||||
import json
|
||||
import yaml
|
||||
import email.mime.multipart
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -44,6 +46,7 @@ log = logging.getLogger(__name__)
|
||||
try:
|
||||
import boto
|
||||
import boto.ec2
|
||||
import boto.ec2.blockdevicemapping as blockdevicemapping
|
||||
import boto.ec2.autoscale as autoscale
|
||||
logging.getLogger('boto').setLevel(logging.CRITICAL)
|
||||
HAS_BOTO = True
|
||||
@ -84,7 +87,7 @@ def exists(name, region=None, key=None, keyid=None, profile=None):
|
||||
|
||||
def get_config(name, region=None, key=None, keyid=None, profile=None):
|
||||
'''
|
||||
Get the configuration for an autoscale group
|
||||
Get the configuration for an autoscale group.
|
||||
|
||||
CLI example::
|
||||
|
||||
@ -131,9 +134,9 @@ def create(name, launch_config_name, availability_zones, min_size, max_size,
|
||||
termination_policies=None, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Create an ELB
|
||||
Create an autoscale group.
|
||||
|
||||
CLI example to create an ELB::
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_asg.create myasg mylc '["us-east-1a", "us-east-1e"]' 1 10 load_balancers='["myelb", "myelb2"]' tags='[{"key": "Name", value="myasg", "propagate_at_launch": True}]'
|
||||
'''
|
||||
@ -184,7 +187,7 @@ def create(name, launch_config_name, availability_zones, min_size, max_size,
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to create ELB {0}'.format(name)
|
||||
msg = 'Failed to create ASG {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
@ -196,7 +199,7 @@ def update(name, launch_config_name, availability_zones, min_size, max_size,
|
||||
termination_policies=None, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Update an ELB
|
||||
Update an autoscale group.
|
||||
|
||||
CLI example::
|
||||
|
||||
@ -253,7 +256,7 @@ def update(name, launch_config_name, availability_zones, min_size, max_size,
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to update ELB {0}'.format(name)
|
||||
msg = 'Failed to update ASG {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
@ -281,6 +284,127 @@ def delete(name, force=False, region=None, key=None, keyid=None, profile=None):
|
||||
return False
|
||||
|
||||
|
||||
def get_cloud_init_mime(cloud_init):
|
||||
'''
|
||||
Get a mime multipart encoded string from a cloud-init dict. Currently
|
||||
supports scripts and cloud-config.
|
||||
'''
|
||||
if isinstance(cloud_init, string_types):
|
||||
cloud_init = json.loads(cloud_init)
|
||||
_cloud_init = email.mime.multipart.MIMEMultipart()
|
||||
if 'scripts' in cloud_init:
|
||||
for script_name, script in cloud_init['scripts'].iteritems():
|
||||
_script = email.mime.text.MIMEText(script, 'x-shellscript')
|
||||
_cloud_init.attach(_script)
|
||||
if 'cloud-config' in cloud_init:
|
||||
cloud_config = cloud_init['cloud-config']
|
||||
_cloud_config = email.mime.text.MIMEText(yaml.dump(cloud_config),
|
||||
'cloud-config')
|
||||
_cloud_init.attach(_cloud_config)
|
||||
return _cloud_init.as_string()
|
||||
|
||||
|
||||
def launch_configuration_exists(name, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Check for a launch configuration's existence.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_asg.launch_configuration_exists mylc
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
lc = conn.get_all_launch_configurations(names=[name])
|
||||
if lc:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def create_launch_configuration(name, image_id=None, key_name=None,
|
||||
security_groups=None, user_data=None,
|
||||
instance_type='m1.small', kernel_id=None,
|
||||
ramdisk_id=None, block_device_mappings=None,
|
||||
instance_monitoring=False, spot_price=None,
|
||||
instance_profile_name=None,
|
||||
ebs_optimized=False,
|
||||
associate_public_ip_address=None,
|
||||
volume_type=None, delete_on_termination=True,
|
||||
iops=None, use_block_device_types=False,
|
||||
region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Create a launch configuration.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_asg.create_launch_configuration mylc image_id=ami-0b9c9f62 key_name='mykey' security_groups='["mygroup"]' instance_type='c3.2xlarge'
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
if isinstance(security_groups, string_types):
|
||||
security_groups = json.loads(security_groups)
|
||||
if isinstance(block_device_mappings, string_types):
|
||||
block_device_mappings = json.loads(block_device_mappings)
|
||||
_bdms = []
|
||||
if block_device_mappings:
|
||||
# Boto requires objects for the mappings and the devices.
|
||||
_block_device_map = blockdevicemapping.BlockDeviceMapping()
|
||||
for block_device_dict in block_device_mappings:
|
||||
for block_device, attributes in block_device_dict.iteritems():
|
||||
_block_device = blockdevicemapping.EBSBlockDeviceType()
|
||||
for attribute, value in attributes.iteritems():
|
||||
setattr(_block_device, attribute, value)
|
||||
_block_device_map[block_device] = _block_device
|
||||
_bdms = [_block_device_map]
|
||||
lc = autoscale.LaunchConfiguration(
|
||||
name=name, image_id=image_id, key_name=key_name,
|
||||
security_groups=security_groups, user_data=user_data,
|
||||
instance_type=instance_type, kernel_id=kernel_id,
|
||||
ramdisk_id=ramdisk_id, block_device_mappings=_bdms,
|
||||
instance_monitoring=instance_monitoring, spot_price=spot_price,
|
||||
instance_profile_name=instance_profile_name,
|
||||
ebs_optimized=ebs_optimized,
|
||||
associate_public_ip_address=associate_public_ip_address,
|
||||
volume_type=volume_type, delete_on_termination=delete_on_termination,
|
||||
iops=iops, use_block_device_types=use_block_device_types)
|
||||
try:
|
||||
conn.create_launch_configuration(lc)
|
||||
log.info('Created LC {0}'.format(name))
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to create LC {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def delete_launch_configuration(name, region=None, key=None, keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Delete a launch configuration.
|
||||
|
||||
CLI example::
|
||||
|
||||
salt myminion boto_asg.delete_launch_configuration mylc
|
||||
'''
|
||||
conn = _get_conn(region, key, keyid, profile)
|
||||
if not conn:
|
||||
return False
|
||||
try:
|
||||
conn.delete_launch_configuration(name)
|
||||
log.info('Deleted LC {0}'.format(name))
|
||||
return True
|
||||
except boto.exception.BotoServerError as e:
|
||||
log.debug(e)
|
||||
msg = 'Failed to delete LC {0}'.format(name)
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def _get_conn(region, key, keyid, profile):
|
||||
'''
|
||||
Get a boto connection to autoscale.
|
||||
|
291
salt/states/boto_lc.py
Normal file
291
salt/states/boto_lc.py
Normal file
@ -0,0 +1,291 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Manage Launch Configurations
|
||||
============================
|
||||
|
||||
.. versionadded:: Helium
|
||||
|
||||
Create and destroy Launch Configurations. Be aware that this interacts with
|
||||
Amazon's services, and so may incur charges.
|
||||
|
||||
A limitation of this module is that you can not modify launch configurations
|
||||
once they have been created. If a launch configuration with the specified name
|
||||
exists, this module will always report success, even if the specified
|
||||
configuration doesn't match. This is due to a limitation in Amazon's launch
|
||||
configuration API, as it only allows launch configurations to be created and
|
||||
deleted.
|
||||
|
||||
Also note that a launch configuration that's in use by an autoscale group can
|
||||
not be deleted until the autoscale group is no longer using it. This may affect
|
||||
the way in which you want to order your states.
|
||||
|
||||
This module uses boto, which can be installed via package, or pip.
|
||||
|
||||
This module accepts explicit autoscale 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::
|
||||
|
||||
asg.keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
asg.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
|
||||
|
||||
Credential information is shared with autoscale groups as launch configurations
|
||||
and autoscale groups are completely dependent on each other.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Ensure mylc exists:
|
||||
boto_lc.present:
|
||||
- name: mylc
|
||||
- image_id: ami-0b9c9f62
|
||||
- key_name: mykey
|
||||
- security_groups:
|
||||
- mygroup
|
||||
- instance_type: m1.small
|
||||
- instance_monitoring: true
|
||||
- block_device_mappings:
|
||||
- '/dev/sda1':
|
||||
size: 20
|
||||
- cloud_init:
|
||||
scripts:
|
||||
'run_salt.sh': |
|
||||
#!/bin/bash
|
||||
|
||||
add-apt-repository -y ppa:saltstack/salt
|
||||
apt-get update
|
||||
apt-get install -y salt-minion
|
||||
salt-call state.highstate
|
||||
- region: us-east-1
|
||||
- keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
- key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
|
||||
# Using a profile from pillars.
|
||||
Ensure mylc exists:
|
||||
boto_lc.present:
|
||||
- name: mylc
|
||||
- image_id: ami-0b9c9f62
|
||||
- profile: myprofile
|
||||
|
||||
# Passing in a profile.
|
||||
Ensure mylc exists:
|
||||
boto_lc.present:
|
||||
- name: mylc
|
||||
- image_id: ami-0b9c9f62
|
||||
- profile:
|
||||
keyid: GKTADJGHEIQSXMKKRBJ08H
|
||||
key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs
|
||||
region: us-east-1
|
||||
'''
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load if boto is available.
|
||||
'''
|
||||
return 'boto_lc' if 'boto_asg.exists' in __salt__ else False
|
||||
|
||||
|
||||
def present(
|
||||
name,
|
||||
image_id,
|
||||
key_name=None,
|
||||
security_groups=None,
|
||||
user_data=None,
|
||||
cloud_init=None,
|
||||
instance_type='m1.small',
|
||||
kernel_id=None,
|
||||
ramdisk_id=None,
|
||||
block_device_mappings=None,
|
||||
instance_monitoring=False,
|
||||
spot_price=None,
|
||||
instance_profile_name=None,
|
||||
ebs_optimized=False,
|
||||
associate_public_ip_address=None,
|
||||
volume_type=None,
|
||||
delete_on_termination=True,
|
||||
iops=None,
|
||||
use_block_device_types=False,
|
||||
region=None,
|
||||
key=None,
|
||||
keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Ensure the launch configuration exists.
|
||||
|
||||
name
|
||||
Name of the launch configuration.
|
||||
|
||||
image_id
|
||||
AMI to use for instances. AMI must exist or creation of the launch
|
||||
configuration will fail.
|
||||
|
||||
key_name
|
||||
Name of the EC2 key pair to use for instances. Key must exist or
|
||||
creation of the launch configuration will fail.
|
||||
|
||||
security_groups
|
||||
List of Names or security group id’s of the security groups with which
|
||||
to associate the EC2 instances or VPC instances, respectively. Security
|
||||
groups must exist, or creation of the launch configuration will fail.
|
||||
|
||||
user_data
|
||||
The user data available to launched EC2 instances.
|
||||
|
||||
cloud_init
|
||||
A dict of cloud_init configuration. Currently supported values:
|
||||
scripts, cloud-config. Mutually exlusive with user_data.
|
||||
|
||||
instance_type
|
||||
The instance type. ex: m1.small.
|
||||
|
||||
kernel_id
|
||||
The kernel id for the instance.
|
||||
|
||||
ramdisk_id
|
||||
The RAM disk ID for the instance.
|
||||
|
||||
block_device_mappings
|
||||
A dict of block device mappings.
|
||||
|
||||
instance_monitoring
|
||||
Whether instances in group are launched with detailed monitoring.
|
||||
|
||||
spot_price
|
||||
The spot price you are bidding. Only applies if you are building an
|
||||
autoscaling group with spot instances.
|
||||
|
||||
instance_profile_name
|
||||
The name or the Amazon Resource Name (ARN) of the instance profile
|
||||
associated with the IAM role for the instance. Instance profile must
|
||||
exist or the creation of the launch configuration will fail.
|
||||
|
||||
ebs_optimized
|
||||
Specifies whether the instance is optimized for EBS I/O (true) or not
|
||||
(false).
|
||||
|
||||
associate_public_ip_address
|
||||
Used for Auto Scaling groups that launch instances into an Amazon
|
||||
Virtual Private Cloud. Specifies whether to assign a public IP address
|
||||
to each instance launched in a Amazon VPC.
|
||||
|
||||
volume_type
|
||||
Undocumented in boto.
|
||||
|
||||
delete_on_termination
|
||||
Undocumented in boto.
|
||||
|
||||
iops
|
||||
Undocumented in boto.
|
||||
|
||||
use_block_device_types
|
||||
Undocumented in boto.
|
||||
|
||||
region
|
||||
The 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.
|
||||
'''
|
||||
if user_data and cloud_init:
|
||||
raise SaltInvocationError('user_data and cloud_init are mutually'
|
||||
' exclusive options.')
|
||||
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
|
||||
exists = __salt__['boto_asg.launch_configuration_exists'](name, region,
|
||||
key, keyid,
|
||||
profile)
|
||||
if not exists:
|
||||
if __opts__['test']:
|
||||
msg = 'Launch configuration set to be created.'
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
if cloud_init:
|
||||
user_data = __salt__['boto_asg.get_cloud_init_mime'](cloud_init)
|
||||
# TODO: Ensure image_id, key_name, security_groups and instance_profile
|
||||
# exist, or throw an invocation error.
|
||||
created = __salt__['boto_asg.create_launch_configuration'](
|
||||
name, image_id, key_name, security_groups, user_data,
|
||||
instance_type, kernel_id, ramdisk_id, block_device_mappings,
|
||||
instance_monitoring, spot_price, instance_profile_name,
|
||||
ebs_optimized, associate_public_ip_address, volume_type,
|
||||
delete_on_termination, iops, use_block_device_types, region, key,
|
||||
keyid, profile)
|
||||
if created:
|
||||
ret['result'] = True
|
||||
ret['changes']['old'] = None
|
||||
ret['changes']['new'] = name
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to create launch configuration.'
|
||||
else:
|
||||
ret['comment'] = 'Launch configuration present.'
|
||||
return ret
|
||||
|
||||
|
||||
def absent(
|
||||
name,
|
||||
region=None,
|
||||
key=None,
|
||||
keyid=None,
|
||||
profile=None):
|
||||
'''
|
||||
Ensure the named launch configuration is deleted.
|
||||
|
||||
name
|
||||
Name of the launch configuration.
|
||||
|
||||
region
|
||||
The 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': {}}
|
||||
exists = __salt__['boto_asg.launch_configuration_exists'](name, region,
|
||||
key, keyid,
|
||||
profile)
|
||||
if exists:
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
ret['comment'] = 'Launch configuration set to be deleted.'
|
||||
return ret
|
||||
deleted = __salt__['boto_asg.delete_launch_configuration'](
|
||||
name, region, key, keyid, profile)
|
||||
if deleted:
|
||||
ret['result'] = True
|
||||
ret['changes']['old'] = name
|
||||
ret['changes']['new'] = None
|
||||
ret['comment'] = 'Deleted launch configuration.'
|
||||
else:
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Failed to delete launch configuration.'
|
||||
else:
|
||||
ret['comment'] = 'Launch configuration does not exist.'
|
||||
return ret
|
Loading…
Reference in New Issue
Block a user