Bring boto modules from develop to 2014.7

This commit is contained in:
Colton Myers 2014-09-03 15:20:23 -06:00
parent 9acf13bf39
commit b565c6e7ec
5 changed files with 378 additions and 110 deletions

View File

@ -392,7 +392,7 @@ def launch_configuration_exists(name, region=None, key=None, keyid=None,
return False
def create_launch_configuration(name, image_id=None, key_name=None,
def create_launch_configuration(name, image_id, key_name=None,
security_groups=None, user_data=None,
instance_type='m1.small', kernel_id=None,
ramdisk_id=None, block_device_mappings=None,

View File

@ -208,7 +208,7 @@ def create_or_update_alarm(
if evaluation_periods:
evaluation_periods = int(evaluation_periods)
if isinstance(dimensions, string_types):
dimensions = json.decodestring(dimensions)
dimensions = json.loads(dimensions)
if not isinstance(dimensions, dict):
log.error("could not parse dimensions argument: must be json encoding of a dict: '{0}'".format(dimensions))
return False

View File

@ -255,12 +255,12 @@ def associate_profile_to_role(profile_name, role_name, region=None, key=None,
else:
try:
conn.add_role_to_instance_profile(profile_name, role_name)
msg = 'Added {0} instance profile to {0} role.'
msg = 'Added {0} instance profile to {1} role.'
log.info(msg.format(profile_name, role_name))
return True
except boto.exception.BotoServerError as e:
log.debug(e)
msg = 'Failed to add {0} instance profile to {0} role.'
msg = 'Failed to add {0} instance profile to {1} role.'
log.error(msg.format(profile_name, role_name))
return False
@ -293,12 +293,12 @@ def disassociate_profile_from_role(profile_name, role_name, region=None,
else:
try:
conn.remove_role_from_instance_profile(profile_name, role_name)
msg = 'Removed {0} instance profile from {0} role.'
msg = 'Removed {0} instance profile from {1} role.'
log.info(msg.format(profile_name, role_name))
return True
except boto.exception.BotoServerError as e:
log.debug(e)
msg = 'Failed to remove {0} instance profile from {0} role.'
msg = 'Failed to remove {0} instance profile from {1} role.'
log.error(msg.format(profile_name, role_name))
return False

View File

@ -36,6 +36,7 @@ Connection module for Amazon Security Groups
# Import Python libs
import logging
from distutils.version import LooseVersion as _LooseVersion
log = logging.getLogger(__name__)
@ -54,14 +55,25 @@ import salt.utils.odict as odict
def __virtual__():
'''
Only load if boto libraries exist.
Only load if boto libraries exist and if boto libraries are greater than
a given version.
'''
required_boto_version = '2.4.0'
# Boto < 2.4.0 GroupOrCIDR objects have different attributes than
# Boto >= 2.4.0 GroupOrCIDR objects
# Differences include no group_id attribute in Boto < 2.4.0 and returning
# a groupId attribute when a GroupOrCIDR object authorizes an IP range
# Support for Boto < 2.4.0 can be added if needed
if not HAS_BOTO:
return False
return True
elif _LooseVersion(boto.__version__) < _LooseVersion(required_boto_version):
return False
else:
return True
def exists(name, region=None, key=None, keyid=None, profile=None):
def exists(name=None, region=None, key=None, keyid=None, profile=None,
vpc_id=None, group_id=None):
'''
Check to see if an security group exists.
@ -72,11 +84,10 @@ def exists(name, region=None, key=None, keyid=None, profile=None):
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
try:
conn.get_all_security_groups([name])
group = _get_group(conn, name, vpc_id, group_id, region)
if group:
return True
except boto.exception.BotoServerError as e:
log.debug(e)
else:
return False
@ -105,8 +116,70 @@ def _split_rules(rules):
return split
def _get_group(conn, name=None, vpc_id=None, group_id=None, region=None):
'''
Get a group object given a name, name and vpc_id or group_id. Return a
boto.ec2.securitygroup.SecurityGroup object if the group is found, else
return None.
'''
if name:
if vpc_id is None:
logging.debug('getting group for {0}'.format(name))
group_filter = {'group-name': name}
filtered_groups = conn.get_all_security_groups(filters=group_filter)
# security groups can have the same name if groups exist in both
# EC2-Classic and EC2-VPC
# iterate through groups to ensure we return the EC2-Classic
# security group
for group in filtered_groups:
# a group in EC2-Classic will have vpc_id set to None
if group.vpc_id is None:
return group
return None
elif vpc_id:
logging.debug('getting group for {0} in vpc_id {1}'.format(name, vpc_id))
group_filter = {'group-name': name, 'vpc_id': vpc_id}
filtered_groups = conn.get_all_security_groups(filters=group_filter)
if len(filtered_groups) == 1:
return filtered_groups[0]
else:
return None
else:
return None
elif group_id:
try:
groups = conn.get_all_security_groups(group_ids=[group_id])
except boto.exception.BotoServerError as e:
log.debug(e)
return None
if len(groups) == 1:
return groups[0]
else:
return None
else:
return None
def get_group_id(name, vpc_id=None, region=None, key=None, keyid=None, profile=None):
'''
Get a Group ID given a Group Name or Group Name and VPC ID
CLI example::
salt myminion boto_secgroup.get_group_id mysecgroup
'''
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
group = _get_group(conn, name, vpc_id, region)
if group:
return group.id
else:
return False
def get_config(name=None, group_id=None, region=None, key=None, keyid=None,
profile=None):
profile=None, vpc_id=None):
'''
Get the configuration for a security group.
@ -117,57 +190,52 @@ def get_config(name=None, group_id=None, region=None, key=None, keyid=None,
conn = _get_conn(region, key, keyid, profile)
if not conn:
return None
if not (name or group_id):
sg = _get_group(conn, name, vpc_id, group_id, region)
if sg:
ret = odict.OrderedDict()
ret['name'] = sg.name
# TODO: add support for vpc_id in return
# ret['vpc_id'] = sg.vpc_id
ret['group_id'] = sg.id
ret['owner_id'] = sg.owner_id
ret['description'] = sg.description
# TODO: add support for tags
_rules = []
for rule in sg.rules:
logging.debug('examining rule {0} for group {1}'.format(rule, sg.id))
attrs = ['ip_protocol', 'from_port', 'to_port', 'grants']
_rule = odict.OrderedDict()
for attr in attrs:
val = getattr(rule, attr)
if not val:
continue
if attr == 'grants':
_grants = []
for grant in val:
logging.debug('examining grant {0} for'.format(grant))
g_attrs = {'name': 'source_group_name',
'owner_id': 'source_group_owner_id',
'group_id': 'source_group_group_id',
'cidr_ip': 'cidr_ip'}
_grant = odict.OrderedDict()
for g_attr, g_attr_map in g_attrs.iteritems():
g_val = getattr(grant, g_attr)
if not g_val:
continue
_grant[g_attr_map] = g_val
_grants.append(_grant)
_rule['grants'] = _grants
elif attr == 'from_port':
_rule[attr] = int(val)
elif attr == 'to_port':
_rule[attr] = int(val)
else:
_rule[attr] = val
_rules.append(_rule)
ret['rules'] = _split_rules(_rules)
return ret
else:
return None
try:
if name:
sg = conn.get_all_security_groups([name])
else:
sg = conn.get_all_security_groups(group_ids=[group_id])
except boto.exception.BotoServerError as e:
msg = 'Failed to get config for security group {0}.'.format(name)
log.error(msg)
log.debug(e)
return {}
sg = sg[0]
ret = odict.OrderedDict()
ret['name'] = name
ret['group_id'] = sg.id
ret['owner_id'] = sg.owner_id
ret['description'] = sg.description
# TODO: add support for tags
_rules = []
for rule in sg.rules:
attrs = ['ip_protocol', 'from_port', 'to_port', 'grants']
_rule = odict.OrderedDict()
for attr in attrs:
val = getattr(rule, attr)
if not val:
continue
if attr == 'grants':
_grants = []
for grant in val:
g_attrs = {'name': 'source_group_name',
'owner_id': 'source_group_owner_id',
'group_id': 'source_group_group_id',
'cidr_ip': 'cidr_ip'}
_grant = odict.OrderedDict()
for g_attr, g_attr_map in g_attrs.iteritems():
g_val = getattr(grant, g_attr)
if not g_val:
continue
_grant[g_attr_map] = g_val
_grants.append(_grant)
_rule['grants'] = _grants
elif attr == 'from_port':
_rule[attr] = int(val)
elif attr == 'to_port':
_rule[attr] = int(val)
else:
_rule[attr] = val
_rules.append(_rule)
ret['rules'] = _split_rules(_rules)
return ret
def create(name, description, vpc_id=None, region=None, key=None, keyid=None,
@ -192,8 +260,8 @@ def create(name, description, vpc_id=None, region=None, key=None, keyid=None,
return False
def delete(name, group_id=None, region=None, key=None, keyid=None,
profile=None):
def delete(name=None, group_id=None, region=None, key=None, keyid=None,
profile=None, vpc_id=None):
'''
Delete an autoscale group.
@ -204,85 +272,107 @@ def delete(name, group_id=None, region=None, key=None, keyid=None,
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
deleted = conn.delete_security_group(name, group_id)
if deleted:
log.info('Deleted security group {0}.'.format(name))
return True
group = _get_group(conn, name, vpc_id, group_id, region)
if group:
deleted = conn.delete_security_group(group_id=group.id)
if deleted:
log.info('Deleted security group {0} with id {1}.'.format(group.name,
group.id))
return True
else:
msg = 'Failed to delete security group {0}.'.format(name)
log.error(msg)
return False
else:
msg = 'Failed to delete security group {0}.'.format(name)
log.error(msg)
log.debug('Security group not found.')
return False
def authorize(name, source_group_name=None,
def authorize(name=None, source_group_name=None,
source_group_owner_id=None, ip_protocol=None,
from_port=None, to_port=None, cidr_ip=None, group_id=None,
source_group_group_id=None, region=None, key=None,
keyid=None, profile=None):
keyid=None, profile=None, vpc_id=None):
'''
Add a new rule to an existing security group.
CLI example::
salt myminion boto_secgroup.authorize mysecgroup ip_protocol=tcp from_port=80 to_port=80 cidr_ip='["10.0.0.0/0", "192.168.0.0/0"]'
salt myminion boto_secgroup.authorize mysecgroup ip_protocol=tcp from_port=80 to_port=80 cidr_ip='['10.0.0.0/8', '192.168.0.0/24']'
'''
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
try:
added = conn.authorize_security_group(
group_name=name, src_security_group_name=source_group_name,
src_security_group_owner_id=source_group_owner_id,
ip_protocol=ip_protocol, from_port=from_port, to_port=to_port,
cidr_ip=cidr_ip, group_id=group_id,
src_security_group_group_id=source_group_group_id)
if added:
log.info('Added rule to security group {0}.'.format(name))
return True
else:
msg = 'Failed to add rule to security group {0}.'.format(name)
group = _get_group(conn, name, vpc_id, group_id, region)
if group:
try:
added = conn.authorize_security_group(
src_security_group_name=source_group_name,
src_security_group_owner_id=source_group_owner_id,
ip_protocol=ip_protocol, from_port=from_port, to_port=to_port,
cidr_ip=cidr_ip, group_id=group.id,
src_security_group_group_id=source_group_group_id)
if added:
log.info('Added rule to security group {0} with id {1}'
.format(group.name, group.id))
return True
else:
msg = ('Failed to add rule to security group {0} with id {1}.'
.format(group.name, group.id))
log.error(msg)
return False
except boto.exception.EC2ResponseError as e:
log.debug(e)
msg = ('Failed to add rule to security group {0} with id {1}.'
.format(group.name, group.id))
log.error(msg)
return False
except boto.exception.EC2ResponseError as e:
log.debug(e)
msg = 'Failed to add rule to security group {0}.'.format(name)
log.error(msg)
else:
log.debug('Failed to add rule to security group.')
return False
def revoke(name, source_group_name=None,
def revoke(name=None, source_group_name=None,
source_group_owner_id=None, ip_protocol=None,
from_port=None, to_port=None, cidr_ip=None, group_id=None,
source_group_group_id=None, region=None, key=None,
keyid=None, profile=None):
keyid=None, profile=None, vpc_id=None):
'''
Remove a rule from an existing security group.
CLI example::
salt myminion boto_secgroup.revoke mysecgroup ip_protocol=tcp from_port=80 to_port=80 cidr_ip='["10.0.0.0/0", "192.168.0.0/0"]'
salt myminion boto_secgroup.revoke mysecgroup ip_protocol=tcp from_port=80 to_port=80 cidr_ip='10.0.0.0/8'
'''
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
try:
revoked = conn.revoke_security_group(
group_name=name, src_security_group_name=source_group_name,
src_security_group_owner_id=source_group_owner_id,
ip_protocol=ip_protocol, from_port=from_port, to_port=to_port,
cidr_ip=cidr_ip, group_id=group_id,
src_security_group_group_id=source_group_group_id)
if revoked:
log.info('Removed rule from security group {0}.'.format(name))
return True
else:
msg = 'Failed to remove rule from security group {0}.'.format(name)
group = _get_group(conn, name, vpc_id, group_id, region)
if group:
try:
revoked = conn.revoke_security_group(
src_security_group_name=source_group_name,
src_security_group_owner_id=source_group_owner_id,
ip_protocol=ip_protocol, from_port=from_port, to_port=to_port,
cidr_ip=cidr_ip, group_id=group.id,
src_security_group_group_id=source_group_group_id)
if revoked:
log.info('Removed rule from security group {0} with id {1}.'
.format(group.name, group.id))
return True
else:
msg = ('Failed to remove rule from security group {0} with id {1}.'
.format(group.name, group.id))
log.error(msg)
return False
except boto.exception.EC2ResponseError as e:
log.debug(e)
msg = ('Failed to remove rule from security group {0} with id {1}.'
.format(group.name, group.id))
log.error(msg)
return False
except boto.exception.EC2ResponseError as e:
log.debug(e)
msg = 'Failed to remove rule from security group {0}.'.format(name)
log.error(msg)
else:
log.debug('Failed to remove rule from security group.')
return False

178
salt/modules/boto_vpc.py Normal file
View File

@ -0,0 +1,178 @@
# -*- coding: utf-8 -*-
'''
Connection module for Amazon VPC
.. versionadded:: Helium
:configuration: 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
A region may also be specified in the configuration::
asg.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
from distutils.version import LooseVersion as _LooseVersion
log = logging.getLogger(__name__)
# Import third party libs
try:
import boto
import boto.vpc
logging.getLogger('boto').setLevel(logging.CRITICAL)
HAS_BOTO = True
except ImportError:
HAS_BOTO = False
from salt._compat import string_types
def __virtual__():
'''
Only load if boto libraries exist and if boto libraries are greater than
a given version.
'''
required_boto_version = '2.8.0'
# the boto_vpc execution module relies on the connect_to_region() method
# which was added in boto 2.8.0
# https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12
if not HAS_BOTO:
return False
elif _LooseVersion(boto.__version__) < _LooseVersion(required_boto_version):
return False
else:
return True
def get_subnet_association(subnets, region=None, key=None, keyid=None,
profile=None):
'''
Given a subnet (aka: a vpc zone identifier) or list of subnets, returns
vpc association.
Returns a VPC ID if the given subnets are associated with the same VPC ID.
Returns False on an error or if the given subnets are associated with
different VPC IDs.
CLI Examples::
.. code-block:: bash
salt myminion boto_vpc.get_subnet_association subnet-61b47516
.. code-block:: bash
salt myminion boto_vpc.get_subnet_association ['subnet-61b47516','subnet-2cb9785b']
'''
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
try:
# subnet_ids=subnets can accept either a string or a list
subnets = conn.get_all_subnets(subnet_ids=subnets)
except boto.exception.BotoServerError as e:
log.debug(e)
return False
# using a set to store vpc_ids - the use of set prevents duplicate
# vpc_id values
vpc_ids = set()
for subnet in subnets:
log.debug('examining subnet id: {0} for vpc_id'.format(subnet.id))
if subnet in subnets:
log.debug('subnet id: {0} is associated with vpc id: {1}'
.format(subnet.id, subnet.vpc_id))
vpc_ids.add(subnet.vpc_id)
if len(vpc_ids) == 1:
vpc_id = vpc_ids.pop()
log.debug('all subnets are associated with vpc id: {0}'.format(vpc_id))
return vpc_id
else:
log.debug('given subnets are associated with fewer than 1 or greater'
' than 1 subnets')
return False
def exists(vpc_id, region=None, key=None, keyid=None, profile=None):
'''
Given a VPC ID, check to see if the given VPC ID exists.
Returns True if the given VPC ID exists and returns False if the given
VPC ID does not exist.
CLI example::
.. code-block:: bash
salt myminion boto_vpc.exists myvpc
'''
conn = _get_conn(region, key, keyid, profile)
if not conn:
return False
try:
conn.get_all_vpcs(vpc_ids=[vpc_id])
return True
except boto.exception.BotoServerError as e:
log.debug(e)
return False
def _get_conn(region, key, keyid, profile):
'''
Get a boto connection to vpc.
'''
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('region', None)
if not region and __salt__['config.option']('vpc.region'):
region = __salt__['config.option']('vpc.region')
if not region:
region = 'us-east-1'
if not key and __salt__['config.option']('vpc.key'):
key = __salt__['config.option']('vpc.key')
if not keyid and __salt__['config.option']('vpc.keyid'):
keyid = __salt__['config.option']('vpc.keyid')
try:
conn = boto.vpc.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 autoscale connection.')
return None
return conn