Merge pull request #27728 from tkwilliams/d.boto_vpc.dhcp_options_stuff

Enablement for using states to manage AWS EC2 instances
This commit is contained in:
Nicole Thomas 2015-10-07 15:41:38 -06:00
commit fafc6ce09f
6 changed files with 803 additions and 132 deletions

View File

@ -362,7 +362,8 @@ def get_zones(region=None, key=None, keyid=None, profile=None):
def find_instances(instance_id=None, name=None, tags=None, region=None,
key=None, keyid=None, profile=None, return_objs=False):
key=None, keyid=None, profile=None, return_objs=False,
in_states=None):
'''
Given instance properties, find and return matching instance ids
@ -378,6 +379,8 @@ def find_instances(instance_id=None, name=None, tags=None, region=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
if not any((instance_id, name, tags)):
return []
try:
filter_parameters = {'filters': {}}
@ -396,15 +399,19 @@ def find_instances(instance_id=None, name=None, tags=None, region=None,
log.debug('The filters criteria {0} matched the following '
'instances:{1}'.format(filter_parameters, instances))
if in_states:
instances = [i for i in instances if i.state in in_states]
log.debug('Limiting instance matches to those in the requested '
'states: {0}'.format(instances))
if instances:
if return_objs:
return instances
return [instance.id for instance in instances]
else:
return False
return []
except boto.exception.BotoServerError as exc:
log.error(exc)
return False
return []
def create_image(ami_name, instance_id=None, instance_name=None, tags=None, region=None,
@ -506,19 +513,19 @@ def terminate(instance_id=None, name=None, region=None,
instances = find_instances(instance_id=instance_id, name=name,
region=region, key=key, keyid=keyid,
profile=profile, return_objs=True)
if instances in (False, None):
if instances in (False, None, []):
return instances
if len(instances) == 1:
instances[0].terminate()
return True
else:
log.warning('refusing to terminate multiple instances at once')
log.warning('Refusing to terminate multiple instances at once')
return False
def get_id(name=None, tags=None, region=None, key=None,
keyid=None, profile=None):
keyid=None, profile=None, in_states=None):
'''
Given instace properties, return the instance id if it exist.
@ -531,7 +538,7 @@ def get_id(name=None, tags=None, region=None, key=None,
'''
instance_ids = find_instances(name=name, tags=tags, region=region, key=key,
keyid=keyid, profile=profile)
keyid=keyid, profile=profile, in_states=in_states)
if instance_ids:
log.info("Instance ids: {0}".format(" ".join(instance_ids)))
if len(instance_ids) == 1:
@ -545,7 +552,7 @@ def get_id(name=None, tags=None, region=None, key=None,
def exists(instance_id=None, name=None, tags=None, region=None, key=None,
keyid=None, profile=None):
keyid=None, profile=None, in_states=None):
'''
Given a instance id, check to see if the given instance id exists.
@ -558,18 +565,27 @@ def exists(instance_id=None, name=None, tags=None, region=None, key=None,
salt myminion boto_ec2.exists myinstance
'''
instances = find_instances(instance_id=instance_id, name=name, tags=tags)
instances = find_instances(instance_id=instance_id, name=name, tags=tags,
in_states=in_states)
if instances:
log.info('instance exists.')
log.info('Instance exists.')
return True
else:
log.warning('instance does not exist.')
log.warning('Instance does not exist.')
return False
def run(image_id, name=None, tags=None, instance_type='m1.small',
key_name=None, security_groups=None, user_data=None, placement=None,
def run(image_id, name=None, tags=None, key_name=None, security_groups=None,
user_data=None, instance_type='m1.small', placement=None,
kernel_id=None, ramdisk_id=None, monitoring_enabled=None, vpc_id=None,
vpc_name=None, subnet_id=None, subnet_name=None, private_ip_address=None,
block_device_map=None, disable_api_termination=None,
instance_initiated_shutdown_behavior=None, placement_group=None,
client_token=None, security_group_ids=None, security_group_names=None,
additional_info=None, tenancy=None, instance_profile_arn=None,
instance_profile_name=None, ebs_optimized=None, network_interfaces=None,
region=None, key=None, keyid=None, profile=None):
#TODO: support multi-instance reservations
'''
Create and start an EC2 instance.
@ -581,18 +597,131 @@ def run(image_id, name=None, tags=None, instance_type='m1.small',
salt myminion boto_ec2.run ami-b80c2b87 name=myinstance
image_id
(string) The ID of the image to run.
name
(string) - The name of the instance.
tags
(dict of key: value pairs) - tags to apply to the instance.
key_name
(string) The name of the key pair with which to launch instances.
security_groups
(list of strings) The names of the EC2 classic security groups with
which to associate instances
user_data
(string) The Base64-encoded MIME user data to be made available to the
instance(s) in this reservation.
instance_type
(string) The type of instance to run. Note that some image types
(e.g. hvm) only run on some instance types.
placement
(string) The Availability Zone to launch the instance into.
kernel_id
(string) The ID of the kernel with which to launch the instances.
ramdisk_id
(string) The ID of the RAM disk with which to launch the instances.
monitoring_enabled
(bool) Enable detailed CloudWatch monitoring on the instance.
vpc_id
(string) - ID of a VPC to bind the instance to. Exclusive with vpc_name.
vpc_name
(string) - Name of a VPC to bind the instance to. Exclusive with vpc_id.
subnet_id
(string) The subnet ID within which to launch the instances for VPC.
subnet_name
(string) The name of a subnet within which to launch the instances for VPC.
private_ip_address
(string) If youre using VPC, you can optionally use this parameter to
assign the instance a specific available IP address from the subnet
(e.g. 10.0.0.25).
block_device_map
(boto.ec2.blockdevicemapping.BlockDeviceMapping) A BlockDeviceMapping
data structure describing the EBS volumes associated with the Image.
disable_api_termination
(bool) If True, the instances will be locked and will not be able to
be terminated via the API.
instance_initiated_shutdown_behavior
(string) Specifies whether the instance stops or terminates on
instance-initiated shutdown. Valid values are: stop, terminate
placement_group
(string) If specified, this is the name of the placement group in
which the instance(s) will be launched.
client_token
(string) Unique, case-sensitive identifier you provide to ensure
idempotency of the request. Maximum 64 ASCII characters.
security_group_ids
(list of strings) The ID(s) of the VPC security groups with which to
associate instances.
security_group_names
(list of strings) The name(s) of the VPC security groups with which to
associate instances.
additional_info
(string) Specifies additional information to make available to the
instance(s).
tenancy
(string) The tenancy of the instance you want to launch. An instance
with a tenancy of dedicated runs on single-tenant hardware and can
only be launched into a VPC. Valid values are:default or dedicated.
NOTE: To use dedicated tenancy you MUST specify a VPC subnet-ID as well.
instance_profile_arn
(string) The Amazon resource name (ARN) of the IAM Instance Profile
(IIP) to associate with the instances.
instance_profile_name
(string) The name of the IAM Instance Profile (IIP) to associate with
the instances.
ebs_optimized
(bool) Whether the instance is optimized for EBS I/O. This
optimization provides dedicated throughput to Amazon EBS and an
optimized configuration stack to provide optimal EBS I/O performance.
This optimization isnt available with all instance types.
network_interfaces
(boto.ec2.networkinterface.NetworkInterfaceCollection) A
NetworkInterfaceCollection data structure containing the ENI
specifications for the instance.
'''
#TODO: support multi-instance reservations
if all((subnet_id, subnet_name)):
raise SaltInvocationError('Only one of subnet_name or subnet_id may be '
'provided.')
if subnet_name:
r = __salt__['boto_vpc.get_resource_id']('subnet', subnet_name,
region=region, key=key,
keyid=keyid, profile=profile)
if 'id' not in r:
log.warning('Couldn\'t resolve subnet name {0}.').format(subnet_name)
return False
subnet_id = r['id']
if all((security_group_ids, security_group_names)):
raise SaltInvocationError('Only one of security_group_ids or '
'security_group_names may be provided.')
if security_group_names:
security_group_ids = []
for sgn in security_group_names:
r = __salt__['boto_secgroup.get_group_id'](sgn, vpc_name=vpc_name,
region=region, key=key,
keyid=keyid, profile=profile)
if not r:
log.warning('Couldn\'t resolve security group name ' + str(sgn))
return False
security_group_ids += [r]
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
reservation = conn.run_instances(image_id, instance_type=instance_type,
key_name=key_name,
security_groups=security_groups,
user_data=user_data,
placement=placement)
reservation = conn.run_instances(image_id, key_name=key_name, security_groups=security_groups,
user_data=user_data, instance_type=instance_type,
placement=placement, kernel_id=kernel_id, ramdisk_id=ramdisk_id,
monitoring_enabled=monitoring_enabled, subnet_id=subnet_id,
private_ip_address=private_ip_address, block_device_map=block_device_map,
disable_api_termination=disable_api_termination,
instance_initiated_shutdown_behavior=instance_initiated_shutdown_behavior,
placement_group=placement_group, client_token=client_token,
security_group_ids=security_group_ids, additional_info=additional_info,
tenancy=tenancy, instance_profile_arn=instance_profile_arn,
instance_profile_name=instance_profile_name, ebs_optimized=ebs_optimized,
network_interfaces=network_interfaces)
if not reservation:
log.warning('instances could not be reserved')
log.warning('Instance could not be reserved')
return False
instance = reservation.instances[0]
@ -606,9 +735,9 @@ def run(image_id, name=None, tags=None, instance_type='m1.small',
instance.add_tag('Name', name)
if tags:
instance.add_tags(tags)
return True
return {'instance_id': instance.id}
else:
log.warning('instance could not be started -- '
log.warning('Instance could not be started -- '
'status is "{0}"'.format(status))
@ -741,7 +870,8 @@ def get_keys(keynames=None, filters=None, region=None, key=None,
return False
def get_attribute(attribute, instance_name=None, instance_id=None, region=None, key=None, keyid=None, profile=None):
def get_attribute(attribute, instance_name=None, instance_id=None, region=None, key=None,
keyid=None, profile=None):
'''
Get an EC2 instance attribute.
@ -772,7 +902,8 @@ def get_attribute(attribute, instance_name=None, instance_id=None, region=None,
'instanceInitiatedShutdownBehavior', 'rootDeviceName', 'blockDeviceMapping', 'productCodes',
'sourceDestCheck', 'groupSet', 'ebsOptimized', 'sriovNetSupport']
if not any((instance_name, instance_id)):
raise SaltInvocationError('At least one of the following must be specified: instance_name or instance_id.')
raise SaltInvocationError('At least one of the following must be specified: '
'instance_name or instance_id.')
if instance_name and instance_id:
raise SaltInvocationError('Both instance_name and instance_id can not be specified in the same command.')
if attribute not in attribute_list:
@ -780,8 +911,12 @@ def get_attribute(attribute, instance_name=None, instance_id=None, region=None,
try:
if instance_name:
instances = find_instances(name=instance_name, region=region, key=key, keyid=keyid, profile=profile)
if len(instances) != 1:
raise CommandExecutionError('Found more than one EC2 instance matching the criteria.')
if len(instances) > 1:
log.error('Found more than one EC2 instance matching the criteria.')
return False
elif len(instances) < 1:
log.error('Found no EC2 instance matching the criteria.')
return False
instance_id = instances[0]
instance_attribute = conn.get_instance_attribute(instance_id, attribute)
if not instance_attribute:

View File

@ -101,14 +101,17 @@ def exists(name=None, region=None, key=None, keyid=None, profile=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
group = _get_group(conn, name, vpc_id, vpc_name, group_id, region, key, keyid, profile)
group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if group:
return True
else:
return False
def _check_vpc(vpc_id, vpc_name, region, key, keyid, profile):
def _check_vpc(vpc_id=None, vpc_name=None, region=None, key=None, keyid=None,
profile=None):
data = __salt__['boto_vpc.get_id'](name=vpc_name, region=region,
key=key, keyid=keyid, profile=profile)
try:
@ -154,17 +157,14 @@ def _get_group(conn=None, name=None, vpc_id=None, vpc_name=None, group_id=None,
if vpc_name and vpc_id:
raise SaltInvocationError('The params \'vpc_id\' and \'vpc_name\' '
'are mutually exclusive.')
if not vpc_id and vpc_name:
if vpc_name:
try:
vpc_id = _check_vpc(vpc_id, vpc_name, region, key, keyid, profile)
vpc_id = _check_vpc(vpc_id=vpc_id, vpc_name=vpc_name, region=region,
key=key, keyid=keyid, profile=profile)
except boto.exception.BotoServerError as e:
log.debug(e)
return None
if name:
if vpc_name:
vpc_id = __salt__['boto_vpc.check_vpc'](vpc_id, vpc_name, region, key, keyid, profile)
if vpc_id is None:
log.debug('getting group for {0}'.format(name))
group_filter = {'group-name': name}
@ -249,15 +249,15 @@ def get_group_id(name, vpc_id=None, vpc_name=None, region=None, key=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
group = _get_group(conn, name, vpc_id, vpc_name, region, key, keyid, profile)
group = _get_group(conn, name, vpc_id=vpc_id, vpc_name=vpc_name,
region=region, key=key, keyid=keyid, profile=profile)
if group:
return group.id
else:
return False
def convert_to_group_ids(groups, vpc_id=None, vpc_name=None, region=None, key=None,
def convert_to_group_ids(groups, vpc_id, vpc_name=None, region=None, key=None,
keyid=None, profile=None):
'''
Given a list of security groups and a vpc_id, convert_to_group_ids will
@ -277,7 +277,9 @@ def convert_to_group_ids(groups, vpc_id=None, vpc_name=None, region=None, key=No
else:
log.debug('calling boto_secgroup.get_group_id for'
' group name {0}'.format(group))
group_id = get_group_id(group, vpc_id, vpc_name, region, key, keyid, profile)
group_id = get_group_id(name=group, vpc_id=vpc_id,
vpc_name=vpc_name, region=region,
key=key, keyid=keyid, profile=profile)
log.debug('group name {0} has group id {1}'.format(
group, group_id)
)
@ -297,7 +299,9 @@ def get_config(name=None, group_id=None, region=None, key=None, keyid=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
sg = _get_group(conn, name, vpc_id, vpc_name, group_id, region, key, keyid, profile)
sg = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if sg:
ret = odict.OrderedDict()
ret['name'] = sg.name
@ -329,7 +333,8 @@ def create(name, description, vpc_id=None, vpc_name=None, region=None, key=None,
if not vpc_id and vpc_name:
try:
vpc_id = _check_vpc(vpc_id, vpc_name, region, key, keyid, profile)
vpc_id = _check_vpc(vpc_id=vpc_id, vpc_name=vpc_name, region=region,
key=key, keyid=keyid, profile=profile)
except boto.exception.BotoServerError as e:
log.debug(e)
return False
@ -355,7 +360,9 @@ def delete(name=None, group_id=None, region=None, key=None, keyid=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
group = _get_group(conn, name, vpc_id, vpc_name, group_id, region, key, keyid, profile)
group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if group:
deleted = conn.delete_security_group(group_id=group.id)
if deleted:
@ -385,7 +392,9 @@ def authorize(name=None, source_group_name=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
group = _get_group(conn, name, vpc_id, vpc_name, group_id, region, key, keyid, profile)
group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if group:
try:
added = None
@ -435,7 +444,9 @@ def revoke(name=None, source_group_name=None,
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
group = _get_group(conn, name, vpc_id, vpc_name, group_id, region, key, keyid, profile)
group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if group:
try:
revoked = None
@ -474,12 +485,10 @@ def revoke(name=None, source_group_name=None,
def _find_vpcs(vpc_id=None, vpc_name=None, cidr=None, tags=None,
region=None, key=None, keyid=None, profile=None):
'''
Given VPC properties, find and return matching VPC ids.
Borrowed from boto_vpc; these could be refactored into a common library
'''
if all((vpc_id, vpc_name)):
raise SaltInvocationError('Only one of vpc_name or vpc_id may be '
'provided.')
@ -562,8 +571,9 @@ def set_tags(tags,
salt myminion boto_secgroup.set_tags "{'TAG1': 'Value1', 'TAG2': 'Value2'}" security_group_name vpc_id=vpc-13435 profile=my_aws_profile
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
secgrp = _get_group(conn, name=name, vpc_id=vpc_id, group_id=group_id, region=region, vpc_name=vpc_name,
key=key, keyid=keyid, profile=profile)
secgrp = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if secgrp:
if isinstance(tags, dict):
@ -625,8 +635,9 @@ def delete_tags(tags,
salt myminion boto_secgroup.delete_tags ['TAG_TO_DELETE1','TAG_TO_DELETE2'] security_group_name vpc_id=vpc-13435 profile=my_aws_profile
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
secgrp = _get_group(conn, name=name, vpc_id=vpc_id, group_id=group_id, region=region, vpc_name=vpc_name,
key=key, keyid=keyid, profile=profile)
secgrp = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name,
group_id=group_id, region=region, key=key, keyid=keyid,
profile=profile)
if secgrp:
if isinstance(tags, list):
tags_to_remove = {}

View File

@ -131,8 +131,8 @@ def __init__(opts):
__utils__['boto.assign_funcs'](__name__, 'vpc')
def check_vpc(vpc_id=None, vpc_name=None, region=None, key=None, keyid=None,
profile=None):
def check_vpc(vpc_id=None, vpc_name=None, region=None, key=None,
keyid=None, profile=None):
'''
Check whether a VPC with the given name or id exists.
Returns the vpc_id or None. Raises SaltInvocationError if
@ -783,10 +783,10 @@ def create_subnet(vpc_id=None, cidr_block=None, vpc_name=None,
except BotoServerError as e:
return {'created': False, 'error': salt.utils.boto.get_error(e)}
return _create_resource('subnet', name=subnet_name, tags=tags,
vpc_id=vpc_id, cidr_block=cidr_block,
region=region, key=key, keyid=keyid,
profile=profile)
return _create_resource('subnet', name=subnet_name, tags=tags, vpc_id=vpc_id,
availability_zone=availability_zone,
cidr_block=cidr_block, region=region, key=key,
keyid=keyid, profile=profile)
def delete_subnet(subnet_id=None, subnet_name=None, region=None, key=None,
@ -1227,6 +1227,45 @@ def create_dhcp_options(domain_name=None, domain_name_servers=None, ntp_servers=
return {'created': False, 'error': salt.utils.boto.get_error(e)}
def get_dhcp_options(dhcp_options_name=None, dhcp_options_id=None,
region=None, key=None, keyid=None, profile=None):
'''
Return a dict with the current values of the requested DHCP options set
CLI Example:
.. code-block:: bash
salt myminion boto_vpc.get_dhcp_options 'myfunnydhcpoptionsname'
.. versionadded:: Boron
'''
if not any((dhcp_options_name, dhcp_options_id)):
raise SaltInvocationError('At least one of the following must be specified: '
'dhcp_options_name, dhcp_options_id.')
if not dhcp_options_id and dhcp_options_name:
dhcp_options_id = _get_resource_id('dhcp_options', dhcp_options_name,
region=region, key=key,
keyid=keyid, profile=profile)
if not dhcp_options_id:
return {'dhcp_options': {}}
try:
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
r = conn.get_all_dhcp_options(dhcp_options_ids=[dhcp_options_id])
except BotoServerError as e:
return {'error': salt.utils.boto.get_error(e)}
if not r:
return {'dhcp_options': None}
keys = ('domain_name', 'domain_name_servers', 'ntp_servers',
'netbios_name_servers', 'netbios_node_type')
return {'dhcp_options': dict((k, r[0].options.get(k)) for k in keys)}
def delete_dhcp_options(dhcp_options_id=None, dhcp_options_name=None,
region=None, key=None, keyid=None, profile=None):
'''

View File

@ -58,7 +58,7 @@ from time import time, sleep
# Import salt libs
import salt.utils.dictupdate as dictupdate
from salt.exceptions import SaltInvocationError
from salt.exceptions import SaltInvocationError, CommandExecutionError
log = logging.getLogger(__name__)
@ -452,3 +452,318 @@ def snapshot_created(name, ami_name, instance_name, wait_until_available=True, w
sleep(5)
return ret
def instance_present(name, instance_name=None, instance_id=None, image_id=None,
image_name=None, tags=None, key_name=None,
security_groups=None, user_data=None, instance_type=None,
placement=None, kernel_id=None, ramdisk_id=None,
vpc_id=None, vpc_name=None, monitoring_enabled=None,
subnet_id=None, subnet_name=None, private_ip_address=None,
block_device_map=None, disable_api_termination=None,
instance_initiated_shutdown_behavior=None,
placement_group=None, client_token=None,
security_group_ids=None, security_group_names=None,
additional_info=None, tenancy=None,
instance_profile_arn=None, instance_profile_name=None,
ebs_optimized=None, network_interfaces=None,
attributes=None, target_state=None, region=None, key=None,
keyid=None, profile=None):
### TODO - implement 'target_state={running, stopped}'
### TODO - implement image_name->image_id lookups
'''
Ensure an EC2 instance is running with the given attributes and state.
name
(string) - The name of the state definition. Recommended that this
match the instance_name attribute (generally the FQDN of the instance).
instance_name
(string) - The name of the instance, generally its FQDN. Exclusive with
'instance_id'.
instance_id
(string) - The ID of the instance (if known). Exclusive with
'instance_name'.
image_id
(string) The ID of the AMI image to run.
image_name
(string) The name of the AMI image to run. NOT IMPLEMENTED.
tags
(dict) - Tags to apply to the instance.
key_name
(string) The name of the key pair with which to launch instances.
security_groups
(list of strings) The names of the EC2 classic security groups with
which to associate instances
user_data
(string) The Base64-encoded MIME user data to be made available to the
instance(s) in this reservation.
instance_type
(string) The EC2 instance size/type. Note that only certain types are
compatible with HVM based AMIs.
placement
(string) The Availability Zone to launch the instance into.
kernel_id
(string) The ID of the kernel with which to launch the instances.
ramdisk_id
(string) The ID of the RAM disk with which to launch the instances.
vpc_id
(string) - The ID of a VPC to attach the instance to.
vpc_name
(string) - The name of a VPC to attach the instance to.
monitoring_enabled
(bool) Enable detailed CloudWatch monitoring on the instance.
subnet_id
(string) The ID of the subnet within which to launch the instances for
VPC.
subnet_name
(string) The name of the subnet within which to launch the instances
for VPC.
private_ip_address
(string) If youre using VPC, you can optionally use this parameter to
assign the instance a specific available IP address from the subnet
(e.g., 10.0.0.25).
block_device_map
(boto.ec2.blockdevicemapping.BlockDeviceMapping) A BlockDeviceMapping
data structure describing the EBS volumes associated with the Image.
disable_api_termination
(bool) If True, the instances will be locked and will not be able to
be terminated via the API.
instance_initiated_shutdown_behavior
(string) Specifies whether the instance stops or terminates on
instance-initiated shutdown. Valid values are:
'stop'
'terminate'
placement_group
(string) If specified, this is the name of the placement group in
which the instance(s) will be launched.
client_token
(string) Unique, case-sensitive identifier you provide to ensure
idempotency of the request. Maximum 64 ASCII characters.
security_group_ids
(list of strings) The IDs of the VPC security groups with which to
associate instances.
security_group_names
(list of strings) The names of the VPC security groups with which to
associate instances.
additional_info
(string) Specifies additional information to make available to the
instance(s).
tenancy
(string) The tenancy of the instance you want to launch. An instance
with a tenancy of dedicated runs on single-tenant hardware and can
only be launched into a VPC. Valid values are:default or dedicated.
NOTE: To use dedicated tenancy you MUST specify a VPC subnet-ID as well.
instance_profile_arn
(string) The Amazon resource name (ARN) of the IAM Instance Profile
(IIP) to associate with the instances.
instance_profile_name
(string) The name of the IAM Instance Profile (IIP) to associate with
the instances.
ebs_optimized
(bool) Whether the instance is optimized for EBS I/O. This
optimization provides dedicated throughput to Amazon EBS and a tuned
configuration stack to provide optimal EBS I/O performance. This
optimization isnt available with all instance types.
network_interfaces
(boto.ec2.networkinterface.NetworkInterfaceCollection) A
NetworkInterfaceCollection data structure containing the ENI
specifications for the instance.
attributes
(dict) - Instance attributes and value to be applied to the instance.
Available options are:
instanceType - A valid instance type (m1.small)
kernel - Kernel ID (None)
ramdisk - Ramdisk ID (None)
userData - Base64 encoded String (None)
disableApiTermination - Boolean (true)
instanceInitiatedShutdownBehavior - stop|terminate
blockDeviceMapping - List of strings - ie: [/dev/sda=false]
sourceDestCheck - Boolean (true)
groupSet - Set of Security Groups or IDs
ebsOptimized - Boolean (false)
sriovNetSupport - String - ie: simple
target_state
(string) - The desired target state of the instance. Available options
are:
running
stopped
region
(string) - Region to connect to.
key
(string) - Secret key to be used.
keyid
(string) - Access key to be used.
profile
(variable) - A dict with region, key and keyid, or a pillar key (string)
that contains a dict with region, key and keyid.
.. versionadded:: Boron
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}
}
_create = False
running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped')
changed_attrs = {}
if not instance_id:
try:
instance_id = __salt__['boto_ec2.get_id'](name=instance_name if instance_name else name,
tags=tags, region=region, key=key, keyid=keyid,
profile=profile, in_states=running_states)
except CommandExecutionError as e:
ret['result'] = None
ret['comment'] = 'Couldn\'t determine current status of instance {0}.'.format(instance_name)
return ret
exists = __salt__['boto_ec2.exists'](instance_id=instance_id, region=region,
key=key, keyid=keyid, profile=profile)
if not exists:
_create = True
else:
instances = __salt__['boto_ec2.find_instances'](instance_id=instance_id, region=region,
key=key, keyid=keyid, profile=profile,
return_objs=True, in_states=running_states)
if not len(instances):
_create = True
if _create:
if __opts__['test']:
ret['comment'] = 'The instance {0} is set to be created.'.format(name)
ret['result'] = None
return ret
r = __salt__['boto_ec2.run'](image_id, instance_name if instance_name else name,
tags=tags, key_name=key_name,
security_groups=security_groups, user_data=user_data,
instance_type=instance_type, placement=placement,
kernel_id=kernel_id, ramdisk_id=ramdisk_id, vpc_id=vpc_id,
vpc_name=vpc_name, monitoring_enabled=monitoring_enabled,
subnet_id=subnet_id, subnet_name=subnet_name,
private_ip_address=private_ip_address,
block_device_map=block_device_map,
disable_api_termination=disable_api_termination,
instance_initiated_shutdown_behavior=instance_initiated_shutdown_behavior,
placement_group=placement_group, client_token=client_token,
security_group_ids=security_group_ids,
security_group_names=security_group_names,
additional_info=additional_info, tenancy=tenancy,
instance_profile_arn=instance_profile_arn,
instance_profile_name=instance_profile_name,
ebs_optimized=ebs_optimized, network_interfaces=network_interfaces,
region=region, key=key, keyid=keyid, profile=profile)
if not r or 'instance_id' not in r:
ret['result'] = False
ret['comment'] = 'Failed to create instance {0}.'.format(instance_name if instance_name else name)
return ret
instance_id = r['instance_id']
ret['changes'] = {'old': {}, 'new': {}}
ret['changes']['old']['instance_id'] = None
ret['changes']['new']['instance_id'] = instance_id
for k, v in attributes.iteritems():
curr = __salt__['boto_ec2.get_attribute'](k, instance_id=instance_id, region=region, key=key,
keyid=keyid, profile=profile)
if isinstance(curr, dict):
curr = {}
if curr and curr.get(k) == v:
continue
else:
if __opts__['test']:
changed_attrs[k] = 'The instance attribute {0} is set to be changed from \'{1}\' to \'{2}\'.'.format(
k, curr.get(k), v)
continue
try:
r = __salt__['boto_ec2.set_attribute'](attribute=k, attribute_value=v,
instance_id=instance_id, region=region,
key=key, keyid=keyid, profile=profile)
except SaltInvocationError as e:
ret['result'] = False
ret['comment'] = 'Failed to set attribute {0} to {1} on instance {2}.'.format(k, v, instance_name)
return ret
ret['changes'] = ret['changes'] if ret['changes'] else {'old': {}, 'new': {}}
ret['changes']['old'][k] = curr.get(k)
ret['changes']['new'][k] = v
if __opts__['test']:
if changed_attrs:
ret['changes']['new'] = changed_attrs
ret['result'] = None
return ret
def instance_absent(name, instance_name=None, instance_id=None,
region=None, key=None, keyid=None, profile=None):
'''
Ensure an EC2 instance does not exist (is stopped and removed).
name
(string) - The name of the state definition.
instance_name
(string) - The name of the instance.
instance_id
(string) - The ID of the instance.
region
(string) - Region to connect to.
key
(string) - Secret key to be used.
keyid
(string) - Access key to be used.
profile
(variable) - A dict with region, key and keyid, or a pillar key (string)
that contains a dict with region, key and keyid.
.. versionadded:: Boron
'''
### TODO - Implement 'force' option?? Would automagically turn off
### 'disableApiTermination', as needed before trying to delete.
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}
}
running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped')
if not instance_id:
try:
instance_id = __salt__['boto_ec2.get_id'](name=instance_name if instance_name else name,
region=region, key=key, keyid=keyid,
profile=profile, in_states=running_states)
except CommandExecutionError as e:
ret['result'] = None
ret['comment'] = 'Couldn\'t determine current status of instance {0}.'.format(instance_name)
return ret
exists = __salt__['boto_ec2.exists'](instance_id=instance_id, region=region,
key=key, keyid=keyid, profile=profile)
if not exists:
ret['result'] = True
ret['comment'] = 'Instance {0} is already gone.'.format(instance_id)
return ret
### Honor 'disableApiTermination' - if you want to override it, first use set_attribute() to turn it off
no_can_do = __salt__['boto_ec2.get_attribute']('disableApiTermination', instance_id=instance_id,
region=region, key=key, keyid=keyid, profile=profile)
if no_can_do.get('disableApiTermination') is True:
ret['result'] = False
ret['comment'] = 'Termination of instance {0} via the API is disabled.'.format(instance_id)
return ret
if __opts__['test']:
ret['comment'] = 'The instance {0} is set to be deleted.'.format(name)
ret['result'] = None
return ret
r = __salt__['boto_ec2.terminate'](instance_id=instance_id, name=instance_name, region=region,
key=key, keyid=keyid, profile=profile)
if not r:
ret['result'] = False
ret['comment'] = 'Failed to terminate instance {0}.'.format(instance_id)
return ret
ret['changes']['old'] = {'instance_id': instance_id}
ret['changes']['new'] = None
return ret

View File

@ -128,15 +128,7 @@ def present(
The ID of the VPC to create the security group in, if any. Exclusive with vpc_name.
vpc_name
The name of the VPC wherein to create the security group, if any. Exclusive with vpc_id.
vpc_name
The name of the VPC to create the security group in, if any.
.. versionadded:: Boron
tags
List of key:value pairs of tags to set on the security group
The name of the VPC to create the security group in, if any. Exlusive with vpc_id.
.. versionadded:: Boron
@ -159,11 +151,15 @@ def present(
A dict with region, key and keyid, or a pillar key (string)
that contains a dict with region, key and keyid.
tags
List of key:value pairs of tags to set on the security group
.. versionadded:: Boron
'''
ret = {'name': name, 'result': True, 'comment': '', 'changes': {}}
_ret = _security_group_present(name, description, vpc_id, vpc_name, region, key,
keyid, profile)
_ret = _security_group_present(name, description, vpc_id=vpc_id,
vpc_name=vpc_name, region=region,
key=key, keyid=keyid, profile=profile)
ret['changes'] = _ret['changes']
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
@ -174,20 +170,14 @@ def present(
rules = []
if not rules_egress:
rules_egress = []
_ret = _rules_present(name, rules, rules_egress, vpc_id, vpc_name, region, key,
keyid, profile)
_ret = _rules_present(name, rules, rules_egress, vpc_id=vpc_id, vpc_name=vpc_name,
region=region, key=key, keyid=keyid, profile=profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
_ret = _tags_present(name=name,
tags=tags,
vpc_id=vpc_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
vpc_name=vpc_name)
_ret = _tags_present(name=name, tags=tags, vpc_id=vpc_id, vpc_name=vpc_name,
region=region, key=key, keyid=keyid, profile=profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
@ -195,15 +185,8 @@ def present(
return ret
def _security_group_present(
name,
description,
vpc_id=None,
vpc_name=None,
region=None,
key=None,
keyid=None,
profile=None):
def _security_group_present(name, description, vpc_id=None, vpc_name=None,
region=None, key=None, keyid=None, profile=None):
'''
given a group name or a group name and vpc id (or vpc name):
1. determine if the group exists
@ -212,17 +195,17 @@ def _security_group_present(
'''
ret = {'result': True, 'comment': '', 'changes': {}}
exists = __salt__['boto_secgroup.exists'](name, region, key, keyid,
profile, vpc_id,
vpc_name=vpc_name)
profile, vpc_id, vpc_name)
if not exists:
if __opts__['test']:
msg = 'Security group {0} is set to be created.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
created = __salt__['boto_secgroup.create'](name, description, vpc_id,
vpc_name, region, key, keyid,
profile)
created = __salt__['boto_secgroup.create'](name=name, description=description,
vpc_id=vpc_id, vpc_name=vpc_name,
region=region, key=key, keyid=keyid,
profile=profile)
if created:
ret['changes']['old'] = {'secgroup': None}
sg = __salt__['boto_secgroup.get_config'](name, None, region, key,
@ -372,16 +355,8 @@ def _get_rule_changes(rules, _rules):
return (to_delete, to_create)
def _rules_present(
name,
rules,
rules_egress,
vpc_id=None,
vpc_name=None,
region=None,
key=None,
keyid=None,
profile=None):
def _rules_present(name, rules, rules_egress, vpc_id=None, vpc_name=None,
region=None, key=None, keyid=None, profile=None):
'''
given a group name or group name and vpc_id (or vpc name):
1. get lists of desired rule changes (using _get_rule_changes)
@ -405,7 +380,8 @@ def _rules_present(
_source_group_name = rule.get('source_group_name', None)
if _source_group_name:
_group_id = __salt__['boto_secgroup.get_group_id'](
_source_group_name, vpc_id, vpc_name, region, key, keyid, profile
name=_source_group_name, vpc_id=vpc_id, vpc_name=vpc_name,
region=region, key=key, keyid=keyid, profile=profile
)
if not _group_id:
msg = ('source_group_name {0} does not map to a valid'
@ -511,10 +487,7 @@ def absent(
The ID of the VPC to remove the security group from, if any. Exclusive with vpc_name.
vpc_name
The name of the VPC wherefrom to delete the security group, if any. Exclusive with vpc_id.
vpc_name
The name of the VPC to create the security group in, if any.
The name of the VPC to remove the security group from, if any. Exclusive with vpc_name.
.. versionadded:: Boron
@ -558,27 +531,15 @@ def absent(
return ret
def _tags_present(name,
tags,
vpc_id=None,
region=None,
key=None,
keyid=None,
profile=None,
vpc_name=None):
def _tags_present(name, tags, vpc_id=None, vpc_name=None, region=None,
key=None, keyid=None, profile=None):
'''
helper function to validate tags are correct
'''
ret = {'result': True, 'comment': '', 'changes': {}}
if tags:
sg = __salt__['boto_secgroup.get_config'](name=name,
group_id=None,
region=region,
key=key,
keyid=keyid,
profile=profile,
vpc_id=vpc_id,
vpc_name=vpc_name)
sg = __salt__['boto_secgroup.get_config'](name, None, region, key,
keyid, profile, vpc_id, vpc_name)
#existing_tags = sg.get('tags')
tags_to_add = tags
tags_to_update = {}

View File

@ -230,6 +230,199 @@ def absent(name, tags=None, region=None, key=None, keyid=None, profile=None):
return ret
def dhcp_options_present(name, dhcp_options_id=None, vpc_name=None, vpc_id=None,
domain_name=None, domain_name_servers=None, ntp_servers=None,
netbios_name_servers=None, netbios_node_type=None,
tags=None, region=None, key=None, keyid=None, profile=None):
'''
Ensure a set of DHCP options with the given settings exist.
Note that the current implementation only SETS values during option set
creation. It is unable to update option sets in place, and thus merely
verifies the set exists via the given name and/or dhcp_options_id param.
name
(string)
Name of the DHCP options.
vpc_name
(string)
Name of a VPC to which the options should be associated. Either
vpc_name or vpc_id must be provided.
vpc_id
(string)
Id of a VPC to which the options should be associated. Either
vpc_name or vpc_id must be provided.
domain_name
(string)
Domain name to be assiciated with this option set.
domain_name_servers
(list of strings)
The IP address(es) of up to four domain name servers.
ntp_servers
(list of strings)
The IP address(es) of up to four desired NTP servers.
netbios_name_servers
(list of strings)
The IP address(es) of up to four NetBIOS name servers.
netbios_node_type
(string)
The NetBIOS node type (1, 2, 4, or 8). For more information about
the allowed values, see RFC 2132. The recommended is 2 at this
time (broadcast and multicast are currently not supported).
tags
(dict of key:value pairs)
A set of tags to be added.
region
(string)
Region to connect to.
key
(string)
Secret key to be used.
keyid
(string)
Access key to be used.
profile
(various)
A dict with region, key and keyid, or a pillar key (string) that
contains a dict with region, key and keyid.
.. versionadded:: Boron
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}
}
_new = {'domain_name': domain_name,
'domain_name_servers': domain_name_servers,
'ntp_servers': ntp_servers,
'netbios_name_servers': netbios_name_servers,
'netbios_node_type': netbios_node_type
}
# boto provides no "update_dhcp_options()" functionality, and you can't delete it if
# it's attached, and you can't detach it if it's the only one, so just check if it's
# there or not, and make no effort to validate it's actual settings... :(
### TODO - add support for multiple sets of DHCP options, and then for "swapping out"
### sets by creating new, mapping, then deleting the old.
r = __salt__['boto_vpc.dhcp_options_exists'](dhcp_options_id=dhcp_options_id,
dhcp_options_name=name,
region=region, key=key, keyid=keyid,
profile=profile)
if 'error' in r:
ret['result'] = False
ret['comment'] = 'Failed to validate DHCP options: {0}.'.format(r['error']['message'])
return ret
if r.get('exists'):
ret['comment'] = 'DHCP options already present.'
return ret
else:
if __opts__['test']:
ret['comment'] = 'DHCP options {0} are set to be created.'.format(name)
ret['result'] = None
return ret
r = __salt__['boto_vpc.create_dhcp_options'](domain_name=domain_name,
domain_name_servers=domain_name_servers,
ntp_servers=ntp_servers,
netbios_name_servers=netbios_name_servers,
netbios_node_type=netbios_node_type,
dhcp_options_name=name, tags=tags,
vpc_id=vpc_id, vpc_name=vpc_name,
region=region, key=key, keyid=keyid,
profile=profile)
if not r.get('created'):
ret['result'] = False
ret['comment'] = 'Failed to create DHCP options: {1}'.format(r['error']['message'])
return ret
ret['changes']['old'] = {'dhcp_options': None}
ret['changes']['new'] = {'dhcp_options': _new}
ret['comment'] = 'DHCP options {0} created.'.format(name)
return ret
def dhcp_options_absent(name=None, dhcp_options_id=None, region=None, key=None, keyid=None, profile=None):
'''
Ensure a set of DHCP options with the given settings exist.
name
(string)
Name of the DHCP options set.
dhcp_options_id
(string)
Id of the DHCP options set.
region
(string)
Region to connect to.
key
(string)
Secret key to be used.
keyid
(string)
Access key to be used.
profile
(various)
A dict with region, key and keyid, or a pillar key (string) that
contains a dict with region, key and keyid.
.. versionadded:: Boron
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}
}
r = __salt__['boto_vpc.get_resource_id']('dhcp_options', name=name,
region=region, key=key,
keyid=keyid, profile=profile)
if 'error' in r:
ret['result'] = False
ret['comment'] = 'Failed to delete DHCP options: {0}.'.format(r['error']['message'])
return ret
_id = r.get('id')
if not _id:
ret['comment'] = 'DHCP options {0} do not exist.'.format(name)
return ret
if __opts__['test']:
ret['comment'] = 'DHCP options {0} are set to be deleted.'.format(name)
ret['result'] = None
return ret
r = __salt__['boto_vpc.delete_dhcp_options'](dhcp_options_id=r['id'], region=region,
key=key, keyid=keyid, profile=profile)
if not r.get('deleted'):
ret['result'] = False
ret['comment'] = 'Failed to delete DHCP options: {0}'.format(r['error']['message'])
return ret
ret['changes']['old'] = {'dhcp_options': _id}
ret['changes']['new'] = {'dhcp_options': None}
ret['comment'] = 'DHCP options {0} deleted.'.format(name)
return ret
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):
@ -576,16 +769,17 @@ def route_table_present(name, vpc_name=None, vpc_id=None, routes=None,
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _routes_present(route_table_name=name, routes=routes, tags=tags, region=region, key=key,
keyid=keyid, profile=profile)
_ret = _routes_present(route_table_name=name, routes=routes, tags=tags,
region=region, key=key, keyid=keyid, profile=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 = _subnets_present(route_table_name=name, subnet_ids=subnet_ids, subnet_names=subnet_names, tags=tags, region=region, key=key,
keyid=keyid, profile=profile)
_ret = _subnets_present(route_table_name=name, subnet_ids=subnet_ids,
subnet_names=subnet_names, tags=tags, region=region,
key=key, keyid=keyid, profile=profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
@ -595,14 +789,16 @@ def route_table_present(name, vpc_name=None, vpc_id=None, routes=None,
return ret
def _route_table_present(name, vpc_name=None, vpc_id=None, tags=None, region=None, key=None, keyid=None, profile=None):
def _route_table_present(name, vpc_name=None, vpc_id=None, tags=None, region=None,
key=None, keyid=None, profile=None):
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}
}
r = __salt__['boto_vpc.get_resource_id'](resource='route_table', name=name, region=region, key=key, keyid=keyid,
r = __salt__['boto_vpc.get_resource_id'](resource='route_table', name=name,
region=region, key=key, keyid=keyid,
profile=profile)
if 'error' in r:
ret['result'] = False
@ -618,8 +814,11 @@ def _route_table_present(name, vpc_name=None, vpc_id=None, tags=None, region=Non
ret['result'] = None
return ret
r = __salt__['boto_vpc.create_route_table'](route_table_name=name, vpc_name=vpc_name, vpc_id=vpc_id, tags=tags,
region=region, key=key, keyid=keyid, profile=profile)
r = __salt__['boto_vpc.create_route_table'](route_table_name=name,
vpc_name=vpc_name,
vpc_id=vpc_id, tags=tags,
region=region, key=key,
keyid=keyid, profile=profile)
if not r.get('created'):
ret['result'] = False
ret['comment'] = 'Failed to create route table: {0}.'.format(r['error']['message'])
@ -640,8 +839,8 @@ def _routes_present(route_table_name, routes, tags=None, region=None, key=None,
'changes': {}
}
route_table = __salt__['boto_vpc.describe_route_table'](route_table_name=route_table_name, tags=tags, region=region,
key=key, keyid=keyid, profile=profile)
route_table = __salt__['boto_vpc.describe_route_table'](route_table_name=route_table_name, tags=tags,
region=region, key=key, keyid=keyid, profile=profile)
if 'error' in route_table:
msg = 'Could not retrieve configuration for route table {0}: {1}`.'.format(route_table_name,
route_table['error']['message'])
@ -669,6 +868,17 @@ def _routes_present(route_table_name, routes, tags=None, region=None, key=None,
ret['result'] = False
return ret
_r['gateway_id'] = r['id']
if i.get('instance_name'):
running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped')
r = __salt__['boto_ec2.get_id'](name=i['instance_name'], region=region,
key=key, keyid=keyid, profile=profile,
in_states=running_states)
if r is None:
msg = 'Instance {0} does not exist.'.format(i['instance_name'])
ret['comment'] = msg
ret['result'] = False
return ret
_r['instance_id'] = r
_routes.append(_r)
to_delete = []