Merge remote-tracking branch 'upstream/devel' into devel

reviewable/pr18780/r1
tobbe 9 years ago
commit 75fed6e6d8

@ -144,7 +144,7 @@ options:
instance_tags: instance_tags:
version_added: "1.0" version_added: "1.0"
description: description:
- a hash/dictionary of tags to add to the new instance; '{"key":"value"}' and '{"key":"value","key":"value"}' - a hash/dictionary of tags to add to the new instance or for for starting/stopping instance by tag; '{"key":"value"}' and '{"key":"value","key":"value"}'
required: false required: false
default: null default: null
aliases: [] aliases: []
@ -229,19 +229,19 @@ options:
exact_count: exact_count:
version_added: "1.5" version_added: "1.5"
description: description:
- An integer value which indicates how many instances that match the 'count_tag' parameter should be running. Instances are either created or terminated based on this value. - An integer value which indicates how many instances that match the 'count_tag' parameter should be running. Instances are either created or terminated based on this value.
required: false required: false
default: null default: null
aliases: [] aliases: []
count_tag: count_tag:
version_added: "1.5" version_added: "1.5"
description: description:
- Used with 'exact_count' to determine how many nodes based on a specific tag criteria should be running. This can be expressed in multiple ways and is shown in the EXAMPLES section. For instance, one can request 25 servers that are tagged with "class=webserver". - Used with 'exact_count' to determine how many nodes based on a specific tag criteria should be running. This can be expressed in multiple ways and is shown in the EXAMPLES section. For instance, one can request 25 servers that are tagged with "class=webserver".
required: false required: false
default: null default: null
aliases: [] aliases: []
author: author:
- "Tim Gerla (@tgerla)" - "Tim Gerla (@tgerla)"
- "Lester Wade (@lwade)" - "Lester Wade (@lwade)"
- "Seth Vidal" - "Seth Vidal"
@ -271,7 +271,7 @@ EXAMPLES = '''
wait: yes wait: yes
wait_timeout: 500 wait_timeout: 500
count: 5 count: 5
instance_tags: instance_tags:
db: postgres db: postgres
monitoring: yes monitoring: yes
vpc_subnet_id: subnet-29e63245 vpc_subnet_id: subnet-29e63245
@ -305,7 +305,7 @@ EXAMPLES = '''
wait: yes wait: yes
wait_timeout: 500 wait_timeout: 500
count: 5 count: 5
instance_tags: instance_tags:
db: postgres db: postgres
monitoring: yes monitoring: yes
vpc_subnet_id: subnet-29e63245 vpc_subnet_id: subnet-29e63245
@ -366,7 +366,7 @@ EXAMPLES = '''
region: us-east-1 region: us-east-1
tasks: tasks:
- name: Launch instance - name: Launch instance
ec2: ec2:
key_name: "{{ keypair }}" key_name: "{{ keypair }}"
group: "{{ security_group }}" group: "{{ security_group }}"
instance_type: "{{ instance_type }}" instance_type: "{{ instance_type }}"
@ -446,6 +446,15 @@ EXAMPLES = '''
vpc_subnet_id: subnet-29e63245 vpc_subnet_id: subnet-29e63245
assign_public_ip: yes assign_public_ip: yes
#
# Start stopped instances specified by tag
#
- local_action:
module: ec2
instance_tags:
Name: ExtraPower
state: running
# #
# Enforce that 5 instances with a tag "foo" are running # Enforce that 5 instances with a tag "foo" are running
# (Highly recommended!) # (Highly recommended!)
@ -474,11 +483,11 @@ EXAMPLES = '''
image: ami-40603AD1 image: ami-40603AD1
wait: yes wait: yes
group: webserver group: webserver
instance_tags: instance_tags:
Name: database Name: database
dbtype: postgres dbtype: postgres
exact_count: 5 exact_count: 5
count_tag: count_tag:
Name: database Name: database
dbtype: postgres dbtype: postgres
vpc_subnet_id: subnet-29e63245 vpc_subnet_id: subnet-29e63245
@ -531,8 +540,8 @@ def find_running_instances_by_count_tag(module, ec2, count_tag, zone=None):
for res in reservations: for res in reservations:
if hasattr(res, 'instances'): if hasattr(res, 'instances'):
for inst in res.instances: for inst in res.instances:
instances.append(inst) instances.append(inst)
return reservations, instances return reservations, instances
@ -543,7 +552,7 @@ def _set_none_to_blank(dictionary):
result[k] = _set_none_to_blank(result[k]) result[k] = _set_none_to_blank(result[k])
elif not result[k]: elif not result[k]:
result[k] = "" result[k] = ""
return result return result
def get_reservations(module, ec2, tags=None, state=None, zone=None): def get_reservations(module, ec2, tags=None, state=None, zone=None):
@ -682,7 +691,7 @@ def create_block_device(module, ec2, volume):
# http://aws.amazon.com/about-aws/whats-new/2013/10/09/ebs-provisioned-iops-maximum-iops-gb-ratio-increased-to-30-1/ # http://aws.amazon.com/about-aws/whats-new/2013/10/09/ebs-provisioned-iops-maximum-iops-gb-ratio-increased-to-30-1/
MAX_IOPS_TO_SIZE_RATIO = 30 MAX_IOPS_TO_SIZE_RATIO = 30
if 'snapshot' not in volume and 'ephemeral' not in volume: if 'snapshot' not in volume and 'ephemeral' not in volume:
if 'volume_size' not in volume: if 'volume_size' not in volume:
module.fail_json(msg = 'Size must be specified when creating a new volume or modifying the root volume') module.fail_json(msg = 'Size must be specified when creating a new volume or modifying the root volume')
if 'snapshot' in volume: if 'snapshot' in volume:
if 'device_type' in volume and volume.get('device_type') == 'io1' and 'iops' not in volume: if 'device_type' in volume and volume.get('device_type') == 'io1' and 'iops' not in volume:
@ -695,7 +704,7 @@ def create_block_device(module, ec2, volume):
if 'encrypted' in volume: if 'encrypted' in volume:
module.fail_json(msg = 'You can not set encyrption when creating a volume from a snapshot') module.fail_json(msg = 'You can not set encyrption when creating a volume from a snapshot')
if 'ephemeral' in volume: if 'ephemeral' in volume:
if 'snapshot' in volume: if 'snapshot' in volume:
module.fail_json(msg = 'Cannot set both ephemeral and snapshot') module.fail_json(msg = 'Cannot set both ephemeral and snapshot')
return BlockDeviceType(snapshot_id=volume.get('snapshot'), return BlockDeviceType(snapshot_id=volume.get('snapshot'),
ephemeral_name=volume.get('ephemeral'), ephemeral_name=volume.get('ephemeral'),
@ -760,18 +769,18 @@ def enforce_count(module, ec2, vpc):
for inst in instance_dict_array: for inst in instance_dict_array:
inst['state'] = "terminated" inst['state'] = "terminated"
terminated_list.append(inst) terminated_list.append(inst)
instance_dict_array = terminated_list instance_dict_array = terminated_list
# ensure all instances are dictionaries # ensure all instances are dictionaries
all_instances = [] all_instances = []
for inst in instances: for inst in instances:
if type(inst) is not dict: if type(inst) is not dict:
inst = get_instance_info(inst) inst = get_instance_info(inst)
all_instances.append(inst) all_instances.append(inst)
return (all_instances, instance_dict_array, changed_instance_ids, changed) return (all_instances, instance_dict_array, changed_instance_ids, changed)
def create_instances(module, ec2, vpc, override_count=None): def create_instances(module, ec2, vpc, override_count=None):
""" """
Creates new instances Creates new instances
@ -824,7 +833,10 @@ def create_instances(module, ec2, vpc, override_count=None):
vpc_id = None vpc_id = None
if vpc_subnet_id: if vpc_subnet_id:
vpc_id = vpc.get_all_subnets(subnet_ids=[vpc_subnet_id])[0].vpc_id if not vpc:
module.fail_json(msg="region must be specified")
else:
vpc_id = vpc.get_all_subnets(subnet_ids=[vpc_subnet_id])[0].vpc_id
else: else:
vpc_id = None vpc_id = None
@ -879,7 +891,7 @@ def create_instances(module, ec2, vpc, override_count=None):
if ebs_optimized: if ebs_optimized:
params['ebs_optimized'] = ebs_optimized params['ebs_optimized'] = ebs_optimized
# 'tenancy' always has a default value, but it is not a valid parameter for spot instance resquest # 'tenancy' always has a default value, but it is not a valid parameter for spot instance resquest
if not spot_price: if not spot_price:
params['tenancy'] = tenancy params['tenancy'] = tenancy
@ -912,7 +924,7 @@ def create_instances(module, ec2, vpc, override_count=None):
groups=group_id, groups=group_id,
associate_public_ip_address=assign_public_ip) associate_public_ip_address=assign_public_ip)
interfaces = boto.ec2.networkinterface.NetworkInterfaceCollection(interface) interfaces = boto.ec2.networkinterface.NetworkInterfaceCollection(interface)
params['network_interfaces'] = interfaces params['network_interfaces'] = interfaces
else: else:
params['subnet_id'] = vpc_subnet_id params['subnet_id'] = vpc_subnet_id
if vpc_subnet_id: if vpc_subnet_id:
@ -922,11 +934,11 @@ def create_instances(module, ec2, vpc, override_count=None):
if volumes: if volumes:
bdm = BlockDeviceMapping() bdm = BlockDeviceMapping()
for volume in volumes: for volume in volumes:
if 'device_name' not in volume: if 'device_name' not in volume:
module.fail_json(msg = 'Device name must be set for volume') module.fail_json(msg = 'Device name must be set for volume')
# Minimum volume size is 1GB. We'll use volume size explicitly set to 0 # Minimum volume size is 1GB. We'll use volume size explicitly set to 0
# to be a signal not to create this volume # to be a signal not to create this volume
if 'volume_size' not in volume or int(volume['volume_size']) > 0: if 'volume_size' not in volume or int(volume['volume_size']) > 0:
bdm[volume['device_name']] = create_block_device(module, ec2, volume) bdm[volume['device_name']] = create_block_device(module, ec2, volume)
@ -1016,7 +1028,7 @@ def create_instances(module, ec2, vpc, override_count=None):
num_running = 0 num_running = 0
wait_timeout = time.time() + wait_timeout wait_timeout = time.time() + wait_timeout
while wait_timeout > time.time() and num_running < len(instids): while wait_timeout > time.time() and num_running < len(instids):
try: try:
res_list = ec2.get_all_instances(instids) res_list = ec2.get_all_instances(instids)
except boto.exception.BotoServerError, e: except boto.exception.BotoServerError, e:
if e.error_code == 'InvalidInstanceID.NotFound': if e.error_code == 'InvalidInstanceID.NotFound':
@ -1029,7 +1041,7 @@ def create_instances(module, ec2, vpc, override_count=None):
for res in res_list: for res in res_list:
num_running += len([ i for i in res.instances if i.state=='running' ]) num_running += len([ i for i in res.instances if i.state=='running' ])
if len(res_list) <= 0: if len(res_list) <= 0:
# got a bad response of some sort, possibly due to # got a bad response of some sort, possibly due to
# stale/cached data. Wait a second and then try again # stale/cached data. Wait a second and then try again
time.sleep(1) time.sleep(1)
continue continue
@ -1141,12 +1153,12 @@ def terminate_instances(module, ec2, instance_ids):
filters={'instance-state-name':'terminated'}): filters={'instance-state-name':'terminated'}):
for inst in res.instances: for inst in res.instances:
instance_dict_array.append(get_instance_info(inst)) instance_dict_array.append(get_instance_info(inst))
return (changed, instance_dict_array, terminated_instance_ids) return (changed, instance_dict_array, terminated_instance_ids)
def startstop_instances(module, ec2, instance_ids, state): def startstop_instances(module, ec2, instance_ids, state, instance_tags):
""" """
Starts or stops a list of existing instances Starts or stops a list of existing instances
@ -1154,6 +1166,8 @@ def startstop_instances(module, ec2, instance_ids, state):
ec2: authenticated ec2 connection object ec2: authenticated ec2 connection object
instance_ids: The list of instances to start in the form of instance_ids: The list of instances to start in the form of
[ {id: <inst-id>}, ..] [ {id: <inst-id>}, ..]
instance_tags: A dict of tag keys and values in the form of
{key: value, ... }
state: Intended state ("running" or "stopped") state: Intended state ("running" or "stopped")
Returns a dictionary of instance information Returns a dictionary of instance information
@ -1162,19 +1176,33 @@ def startstop_instances(module, ec2, instance_ids, state):
If the instance was not able to change state, If the instance was not able to change state,
"changed" will be set to False. "changed" will be set to False.
Note that if instance_ids and instance_tags are both non-empty,
this method will process the intersection of the two
""" """
wait = module.params.get('wait') wait = module.params.get('wait')
wait_timeout = int(module.params.get('wait_timeout')) wait_timeout = int(module.params.get('wait_timeout'))
changed = False changed = False
instance_dict_array = [] instance_dict_array = []
if not isinstance(instance_ids, list) or len(instance_ids) < 1: if not isinstance(instance_ids, list) or len(instance_ids) < 1:
module.fail_json(msg='instance_ids should be a list of instances, aborting') # Fail unless the user defined instance tags
if not instance_tags:
module.fail_json(msg='instance_ids should be a list of instances, aborting')
# To make an EC2 tag filter, we need to prepend 'tag:' to each key.
# An empty filter does no filtering, so it's safe to pass it to the
# get_all_instances method even if the user did not specify instance_tags
filters = {}
if instance_tags:
for key, value in instance_tags.items():
filters["tag:" + key] = value
# Check that our instances are not in the state we want to take
# Check (and eventually change) instances attributes and instances state # Check (and eventually change) instances attributes and instances state
running_instances_array = [] running_instances_array = []
for res in ec2.get_all_instances(instance_ids): for res in ec2.get_all_instances(instance_ids, filters=filters):
for inst in res.instances: for inst in res.instances:
# Check "source_dest_check" attribute # Check "source_dest_check" attribute
@ -1281,7 +1309,7 @@ def main():
except boto.exception.NoAuthHandlerFound, e: except boto.exception.NoAuthHandlerFound, e:
module.fail_json(msg = str(e)) module.fail_json(msg = str(e))
else: else:
module.fail_json(msg="region must be specified") vpc = None
tagged_instances = [] tagged_instances = []
@ -1295,11 +1323,12 @@ def main():
(changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids) (changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids)
elif state in ('running', 'stopped'): elif state in ('running', 'stopped'):
instance_ids = module.params['instance_ids'] instance_ids = module.params.get('instance_ids')
if not instance_ids: instance_tags = module.params.get('instance_tags')
module.fail_json(msg='instance_ids list is requried for %s state' % state) if not (isinstance(instance_ids, list) or isinstance(instance_tags, dict)):
module.fail_json(msg='running list needs to be a list of instances or set of tags to run: %s' % instance_ids)
(changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state) (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state, instance_tags)
elif state == 'present': elif state == 'present':
# Changed is always set to true when provisioning new instances # Changed is always set to true when provisioning new instances

@ -126,6 +126,13 @@ options:
version_added: "1.9" version_added: "1.9"
default: yes default: yes
required: False required: False
termination_policies:
description:
- An ordered list of criteria used for selecting instances to be removed from the Auto Scaling group when reducing capacity.
required: false
default: Default. Eg, when used to create a new autoscaling group, the Default value is used. When used to change an existent autoscaling group, the current termination policies are mantained
choices: ['OldestInstance', 'NewestInstance', 'OldestLaunchConfiguration', 'ClosestToNextInstanceHour', 'Default']
version_added: "2.0"
extends_documentation_fragment: aws extends_documentation_fragment: aws
""" """
@ -421,7 +428,8 @@ def create_autoscaling_group(connection, module):
tags=asg_tags, tags=asg_tags,
health_check_period=health_check_period, health_check_period=health_check_period,
health_check_type=health_check_type, health_check_type=health_check_type,
default_cooldown=default_cooldown) default_cooldown=default_cooldown,
termination_policies=termination_policies)
try: try:
connection.create_auto_scaling_group(ag) connection.create_auto_scaling_group(ag)
@ -783,7 +791,8 @@ def main():
health_check_period=dict(type='int', default=300), health_check_period=dict(type='int', default=300),
health_check_type=dict(default='EC2', choices=['EC2', 'ELB']), health_check_type=dict(default='EC2', choices=['EC2', 'ELB']),
default_cooldown=dict(type='int', default=300), default_cooldown=dict(type='int', default=300),
wait_for_instances=dict(type='bool', default=True) wait_for_instances=dict(type='bool', default=True),
termination_policies=dict(type='list', default=None)
), ),
) )

@ -22,10 +22,12 @@ description:
- This module associates AWS EC2 elastic IP addresses with instances - This module associates AWS EC2 elastic IP addresses with instances
version_added: "1.4" version_added: "1.4"
options: options:
instance_id: device_id:
description: description:
- The EC2 instance id - The id of the device for the EIP. Can be an EC2 Instance id or Elastic Network Interface (ENI) id.
required: false required: false
aliases: [ instance_id ]
version_added: "2.0"
public_ip: public_ip:
description: description:
- The elastic IP address to associate with the instance. - The elastic IP address to associate with the instance.
@ -56,9 +58,15 @@ options:
required: false required: false
default: false default: false
version_added: "1.6" version_added: "1.6"
release_on_disassociation:
description:
- whether or not to automatically release the EIP when it is disassociated
required: false
default: false
version_added: "2.0"
extends_documentation_fragment: aws extends_documentation_fragment: aws
author: "Lorin Hochstein (@lorin) <lorin@nimbisservices.com>" author: "Lorin Hochstein (@lorin) <lorin@nimbisservices.com>"
author: "Rick Mendes (@rickmendes) <rmendes@illumina.com>"
notes: notes:
- This module will return C(public_ip) on success, which will contain the - This module will return C(public_ip) on success, which will contain the
public IP address associated with the instance. public IP address associated with the instance.
@ -66,35 +74,36 @@ notes:
the cloud instance is reachable via the new address. Use wait_for and the cloud instance is reachable via the new address. Use wait_for and
pause to delay further playbook execution until the instance is reachable, pause to delay further playbook execution until the instance is reachable,
if necessary. if necessary.
- This module returns multiple changed statuses on disassociation or release.
It returns an overall status based on any changes occuring. It also returns
individual changed statuses for disassociation and release.
''' '''
EXAMPLES = ''' EXAMPLES = '''
- name: associate an elastic IP with an instance - name: associate an elastic IP with an instance
ec2_eip: instance_id=i-1212f003 ip=93.184.216.119 ec2_eip: device_id=i-1212f003 ip=93.184.216.119
- name: associate an elastic IP with a device
ec2_eip: device_id=eni-c8ad70f3 ip=93.184.216.119
- name: disassociate an elastic IP from an instance - name: disassociate an elastic IP from an instance
ec2_eip: instance_id=i-1212f003 ip=93.184.216.119 state=absent ec2_eip: device_id=i-1212f003 ip=93.184.216.119 state=absent
- name: disassociate an elastic IP with a device
ec2_eip: device_id=eni-c8ad70f3 ip=93.184.216.119 state=absent
- name: allocate a new elastic IP and associate it with an instance - name: allocate a new elastic IP and associate it with an instance
ec2_eip: instance_id=i-1212f003 ec2_eip: device_id=i-1212f003
- name: allocate a new elastic IP without associating it to anything - name: allocate a new elastic IP without associating it to anything
action: ec2_eip action: ec2_eip
register: eip register: eip
- name: output the IP - name: output the IP
debug: msg="Allocated IP is {{ eip.public_ip }}" debug: msg="Allocated IP is {{ eip.public_ip }}"
- name: another way of allocating an elastic IP without associating it to anything - name: another way of allocating an elastic IP without associating it to anything
ec2_eip: state='present' ec2_eip: state='present'
- name: provision new instances with ec2 - name: provision new instances with ec2
ec2: keypair=mykey instance_type=c1.medium image=emi-40603AD1 wait=yes''' ec2: keypair=mykey instance_type=c1.medium image=emi-40603AD1 wait=yes'''
''' group=webserver count=3 ''' group=webserver count=3
register: ec2 register: ec2
- name: associate new elastic IPs with each of the instances - name: associate new elastic IPs with each of the instances
ec2_eip: "instance_id={{ item }}" ec2_eip: "device_id={{ item }}"
with_items: ec2.instance_ids with_items: ec2.instance_ids
- name: allocate a new elastic IP inside a VPC in us-west-2 - name: allocate a new elastic IP inside a VPC in us-west-2
ec2_eip: region=us-west-2 in_vpc=yes ec2_eip: region=us-west-2 in_vpc=yes
register: eip register: eip
@ -112,27 +121,27 @@ except ImportError:
class EIPException(Exception): class EIPException(Exception):
pass pass
def associate_ip_and_device(ec2, address, device_id, check_mode, isinstance=True):
def associate_ip_and_instance(ec2, address, instance_id, check_mode): if address_is_associated_with_device(ec2, address, device_id, isinstance):
if address_is_associated_with_instance(ec2, address, instance_id):
return {'changed': False} return {'changed': False}
# If we're in check mode, nothing else to do # If we're in check mode, nothing else to do
if not check_mode: if not check_mode:
if address.domain == 'vpc': if isinstance:
res = ec2.associate_address(instance_id, if address.domain == "vpc":
allocation_id=address.allocation_id) res = ec2.associate_address(device_id, allocation_id=address.allocation_id)
else:
res = ec2.associate_address(device_id, public_ip=address.public_ip)
else: else:
res = ec2.associate_address(instance_id, res = ec2.associate_address(network_interface_id=device_id, allocation_id=address.allocation_id)
public_ip=address.public_ip)
if not res: if not res:
raise EIPException('association failed') raise EIPException('association failed')
return {'changed': True} return {'changed': True}
def disassociate_ip_and_instance(ec2, address, instance_id, check_mode): def disassociate_ip_and_device(ec2, address, device_id, check_mode, isinstance=True):
if not address_is_associated_with_instance(ec2, address, instance_id): if not address_is_associated_with_device(ec2, address, device_id, isinstance):
return {'changed': False} return {'changed': False}
# If we're in check mode, nothing else to do # If we're in check mode, nothing else to do
@ -157,24 +166,33 @@ def _find_address_by_ip(ec2, public_ip):
raise raise
def _find_address_by_instance_id(ec2, instance_id): def _find_address_by_device_id(ec2, device_id, isinstance=True):
addresses = ec2.get_all_addresses(None, {'instance-id': instance_id}) if isinstance:
addresses = ec2.get_all_addresses(None, {'instance-id': device_id})
else:
addresses = ec2.get_all_addresses(None, {'network-interface-id': device_id})
if addresses: if addresses:
return addresses[0] return addresses[0]
def find_address(ec2, public_ip, instance_id): def find_address(ec2, public_ip, device_id, isinstance=True):
""" Find an existing Elastic IP address """ """ Find an existing Elastic IP address """
if public_ip: if public_ip:
return _find_address_by_ip(ec2, public_ip) return _find_address_by_ip(ec2, public_ip)
elif instance_id: elif device_id and isinstance:
return _find_address_by_instance_id(ec2, instance_id) return _find_address_by_device_id(ec2, device_id)
elif device_id:
return _find_address_by_device_id(ec2, device_id, isinstance=False)
def address_is_associated_with_instance(ec2, address, instance_id): def address_is_associated_with_device(ec2, address, device_id, isinstance=True):
""" Check if the elastic IP is currently associated with the instance """ """ Check if the elastic IP is currently associated with the device """
address = ec2.get_all_addresses(address.public_ip)
if address: if address:
return address and address.instance_id == instance_id if isinstance:
return address and address[0].instance_id == device_id
else:
return address and address[0].network_interface_id == device_id
return False return False
@ -185,7 +203,7 @@ def allocate_address(ec2, domain, reuse_existing_ip_allowed):
all_addresses = ec2.get_all_addresses(filters=domain_filter) all_addresses = ec2.get_all_addresses(filters=domain_filter)
unassociated_addresses = [a for a in all_addresses unassociated_addresses = [a for a in all_addresses
if not a.instance_id] if not a.device_id]
if unassociated_addresses: if unassociated_addresses:
return unassociated_addresses[0] return unassociated_addresses[0]
@ -203,21 +221,33 @@ def release_address(ec2, address, check_mode):
return {'changed': True} return {'changed': True}
def find_instance(ec2, instance_id): def find_device(ec2, device_id, isinstance=True):
""" Attempt to find the EC2 instance and return it """ """ Attempt to find the EC2 instance and return it """
reservations = ec2.get_all_reservations(instance_ids=[instance_id]) if isinstance:
try:
reservations = ec2.get_all_reservations(instance_ids=[device_id])
except boto.exception.EC2ResponseError, e:
module.fail_json(msg=str(e))
if len(reservations) == 1:
instances = reservations[0].instances
if len(instances) == 1:
return instances[0]
else:
try:
interfaces = ec2.get_all_network_interfaces(network_interface_ids=[device_id])
except boto.exception.EC2ResponseError, e:
module.fail_json(msg=str(e))
if len(reservations) == 1: if len(interfaces) == 1:
instances = reservations[0].instances return interfaces[0]
if len(instances) == 1:
return instances[0]
raise EIPException("could not find instance" + instance_id) raise EIPException("could not find instance" + device_id)
def ensure_present(ec2, domain, address, instance_id, def ensure_present(ec2, domain, address, device_id,
reuse_existing_ip_allowed, check_mode): reuse_existing_ip_allowed, check_mode, isinstance=True):
changed = False changed = False
# Return the EIP object since we've been given a public IP # Return the EIP object since we've been given a public IP
@ -228,28 +258,39 @@ def ensure_present(ec2, domain, address, instance_id,
address = allocate_address(ec2, domain, reuse_existing_ip_allowed) address = allocate_address(ec2, domain, reuse_existing_ip_allowed)
changed = True changed = True
if instance_id: if device_id:
# Allocate an IP for instance since no public_ip was provided # Allocate an IP for instance since no public_ip was provided
instance = find_instance(ec2, instance_id) if isinstance:
instance = find_device(ec2, device_id)
# Associate address object (provided or allocated) with instance
assoc_result = associate_ip_and_device(ec2, address, device_id,
check_mode)
else:
instance = find_device(ec2, device_id, isinstance=False)
# Associate address object (provided or allocated) with instance
assoc_result = associate_ip_and_device(ec2, address, device_id,
check_mode, isinstance=False)
if instance.vpc_id: if instance.vpc_id:
domain = 'vpc' domain = 'vpc'
# Associate address object (provided or allocated) with instance
assoc_result = associate_ip_and_instance(ec2, address, instance_id,
check_mode)
changed = changed or assoc_result['changed'] changed = changed or assoc_result['changed']
return {'changed': changed, 'public_ip': address.public_ip} return {'changed': changed, 'public_ip': address.public_ip}
def ensure_absent(ec2, domain, address, instance_id, check_mode): def ensure_absent(ec2, domain, address, device_id, check_mode, isinstance=True):
if not address: if not address:
return {'changed': False} return {'changed': False}
# disassociating address from instance # disassociating address from instance
if instance_id: if device_id:
return disassociate_ip_and_instance(ec2, address, instance_id, if isinstance:
check_mode) return disassociate_ip_and_device(ec2, address, device_id,
check_mode)
else:
return disassociate_ip_and_device(ec2, address, device_id,
check_mode, isinstance=False)
# releasing address # releasing address
else: else:
return release_address(ec2, address, check_mode) return release_address(ec2, address, check_mode)
@ -258,13 +299,14 @@ def ensure_absent(ec2, domain, address, instance_id, check_mode):
def main(): def main():
argument_spec = ec2_argument_spec() argument_spec = ec2_argument_spec()
argument_spec.update(dict( argument_spec.update(dict(
instance_id=dict(required=False), device_id=dict(required=False, aliases=['instance_id']),
public_ip=dict(required=False, aliases=['ip']), public_ip=dict(required=False, aliases=['ip']),
state=dict(required=False, default='present', state=dict(required=False, default='present',
choices=['present', 'absent']), choices=['present', 'absent']),
in_vpc=dict(required=False, type='bool', default=False), in_vpc=dict(required=False, type='bool', default=False),
reuse_existing_ip_allowed=dict(required=False, type='bool', reuse_existing_ip_allowed=dict(required=False, type='bool',
default=False), default=False),
release_on_disassociation=dict(required=False, type='bool', default=False),
wait_timeout=dict(default=300), wait_timeout=dict(default=300),
)) ))
@ -278,28 +320,52 @@ def main():
ec2 = ec2_connect(module) ec2 = ec2_connect(module)
instance_id = module.params.get('instance_id') device_id = module.params.get('device_id')
public_ip = module.params.get('public_ip') public_ip = module.params.get('public_ip')
state = module.params.get('state') state = module.params.get('state')
in_vpc = module.params.get('in_vpc') in_vpc = module.params.get('in_vpc')
domain = 'vpc' if in_vpc else None domain = 'vpc' if in_vpc else None
reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed') reuse_existing_ip_allowed = module.params.get('reuse_existing_ip_allowed')
release_on_disassociation = module.params.get('release_on_disassociation')
if device_id and device_id.startswith('i-'):
is_instance=True
elif device_id:
is_instance=False
try: try:
address = find_address(ec2, public_ip, instance_id) if device_id:
address = find_address(ec2, public_ip, device_id, isinstance=is_instance)
else:
address = False
if state == 'present': if state == 'present':
result = ensure_present(ec2, domain, address, instance_id, if device_id:
result = ensure_present(ec2, domain, address, device_id,
reuse_existing_ip_allowed, reuse_existing_ip_allowed,
module.check_mode) module.check_mode, isinstance=is_instance)
else:
address = allocate_address(ec2, domain, reuse_existing_ip_allowed)
result = {'changed': True, 'public_ip': address.public_ip}
else: else:
result = ensure_absent(ec2, domain, address, instance_id, module.check_mode) if device_id:
disassociated = ensure_absent(ec2, domain, address, device_id, module.check_mode, isinstance=is_instance)
if release_on_disassociation and disassociated['changed']:
released = release_address(ec2, address, module.check_mode)
result = { 'changed': True, 'disassociated': disassociated, 'released': released }
else:
result = { 'changed': disassociated['changed'], 'disassociated': disassociated, 'released': { 'changed': False } }
else:
address = find_address(ec2, public_ip, None)
released = release_address(ec2, address, module.check_mode)
result = { 'changed': released['changed'], 'disassociated': { 'changed': False }, 'released': released }
except (boto.exception.EC2ResponseError, EIPException) as e: except (boto.exception.EC2ResponseError, EIPException) as e:
module.fail_json(msg=str(e)) module.fail_json(msg=str(e))
module.exit_json(**result) module.exit_json(**result)
# import module snippets # import module snippets
from ansible.module_utils.basic import * # noqa from ansible.module_utils.basic import * # noqa
from ansible.module_utils.ec2 import * # noqa from ansible.module_utils.ec2 import * # noqa

@ -573,8 +573,8 @@ class ElbManager(object):
# N.B. string manipulations on protocols below (str(), upper()) is to # N.B. string manipulations on protocols below (str(), upper()) is to
# ensure format matches output from ELB API # ensure format matches output from ELB API
listener_list = [ listener_list = [
listener['load_balancer_port'], int(listener['load_balancer_port']),
listener['instance_port'], int(listener['instance_port']),
str(listener['protocol'].upper()), str(listener['protocol'].upper()),
] ]

@ -124,7 +124,7 @@ options:
version_added: "2.0" version_added: "2.0"
classic_link_vpc_security_groups: classic_link_vpc_security_groups:
description: description:
- A list of security group ids with which to associate the ClassicLink VPC instances. - A list of security group id's with which to associate the ClassicLink VPC instances.
required: false required: false
default: null default: null
version_added: "2.0" version_added: "2.0"

@ -146,6 +146,7 @@ EXAMPLES = '''
# Example: Launch an instance and then add a volume if not already attached # Example: Launch an instance and then add a volume if not already attached
# * Volume will be created with the given name if not already created. # * Volume will be created with the given name if not already created.
# * Nothing will happen if the volume is already attached. # * Nothing will happen if the volume is already attached.
# * Requires Ansible 2.0
- ec2: - ec2:
keypair: "{{ keypair }}" keypair: "{{ keypair }}"

@ -610,16 +610,16 @@ def await_resource(conn, resource, status, module):
while wait_timeout > time.time() and resource.status != status: while wait_timeout > time.time() and resource.status != status:
time.sleep(5) time.sleep(5)
if wait_timeout <= time.time(): if wait_timeout <= time.time():
module.fail_json(msg="Timeout waiting for resource %s" % resource.id) module.fail_json(msg="Timeout waiting for RDS resource %s" % resource.name)
if module.params.get('command') == 'snapshot': if module.params.get('command') == 'snapshot':
# Temporary until all the rds2 commands have their responses parsed # Temporary until all the rds2 commands have their responses parsed
if resource.name is None: if resource.name is None:
module.fail_json(msg="Problem with snapshot %s" % resource.snapshot) module.fail_json(msg="There was a problem waiting for RDS snapshot %s" % resource.snapshot)
resource = conn.get_db_snapshot(resource.name) resource = conn.get_db_snapshot(resource.name)
else: else:
# Temporary until all the rds2 commands have their responses parsed # Temporary until all the rds2 commands have their responses parsed
if resource.name is None: if resource.name is None:
module.fail_json(msg="Problem with instance %s" % resource.instance) module.fail_json(msg="There was a problem waiting for RDS instance %s" % resource.instance)
resource = conn.get_db_instance(resource.name) resource = conn.get_db_instance(resource.name)
if resource is None: if resource is None:
break break
@ -653,7 +653,7 @@ def create_db_instance(module, conn):
module.params.get('username'), module.params.get('password'), **params) module.params.get('username'), module.params.get('password'), **params)
changed = True changed = True
except RDSException, e: except RDSException, e:
module.fail_json(msg="failed to create instance: %s" % e.message) module.fail_json(msg="Failed to create instance: %s" % e.message)
if module.params.get('wait'): if module.params.get('wait'):
resource = await_resource(conn, result, 'available', module) resource = await_resource(conn, result, 'available', module)
@ -680,7 +680,7 @@ def replicate_db_instance(module, conn):
result = conn.create_db_instance_read_replica(instance_name, source_instance, **params) result = conn.create_db_instance_read_replica(instance_name, source_instance, **params)
changed = True changed = True
except RDSException, e: except RDSException, e:
module.fail_json(msg="failed to create replica instance: %s " % e.message) module.fail_json(msg="Failed to create replica instance: %s " % e.message)
if module.params.get('wait'): if module.params.get('wait'):
resource = await_resource(conn, result, 'available', module) resource = await_resource(conn, result, 'available', module)
@ -719,7 +719,7 @@ def delete_db_instance_or_snapshot(module, conn):
else: else:
result = conn.delete_db_snapshot(snapshot) result = conn.delete_db_snapshot(snapshot)
except RDSException, e: except RDSException, e:
module.fail_json(msg="failed to delete instance: %s" % e.message) module.fail_json(msg="Failed to delete instance: %s" % e.message)
# If we're not waiting for a delete to complete then we're all done # If we're not waiting for a delete to complete then we're all done
# so just return # so just return
@ -745,11 +745,11 @@ def facts_db_instance_or_snapshot(module, conn):
snapshot = module.params.get('snapshot') snapshot = module.params.get('snapshot')
if instance_name and snapshot: if instance_name and snapshot:
module.fail_json(msg="facts must be called with either instance_name or snapshot, not both") module.fail_json(msg="Facts must be called with either instance_name or snapshot, not both")
if instance_name: if instance_name:
resource = conn.get_db_instance(instance_name) resource = conn.get_db_instance(instance_name)
if not resource: if not resource:
module.fail_json(msg="DB Instance %s does not exist" % instance_name) module.fail_json(msg="DB instance %s does not exist" % instance_name)
if snapshot: if snapshot:
resource = conn.get_db_snapshot(snapshot) resource = conn.get_db_snapshot(snapshot)
if not resource: if not resource:
@ -1037,7 +1037,7 @@ def main():
region, ec2_url, aws_connect_params = get_aws_connection_info(module) region, ec2_url, aws_connect_params = get_aws_connection_info(module)
if not region: if not region:
module.fail_json(msg="region not specified and unable to determine region from EC2_REGION.") module.fail_json(msg="Region not specified. Unable to determine region from EC2_REGION.")
# connect to the rds endpoint # connect to the rds endpoint
if has_rds2: if has_rds2:

@ -93,6 +93,12 @@ options:
- Keyname of the object inside the bucket. Can be used to create "virtual directories", see examples. - Keyname of the object inside the bucket. Can be used to create "virtual directories", see examples.
required: false required: false
default: null default: null
permission:
description:
- This option let's the user set the canned permissions on the object/bucket that are created. The permissions that can be set are 'private', 'public-read', 'public-read-write', 'authenticated-read'.
required: false
default: private
version_added: "2.0"
prefix: prefix:
description: description:
- Limits the response to keys that begin with the specified prefix for list mode - Limits the response to keys that begin with the specified prefix for list mode
@ -167,7 +173,7 @@ EXAMPLES = '''
- s3: bucket=mybucket mode=list prefix=/my/desired/ marker=/my/desired/0023.txt max_keys=472 - s3: bucket=mybucket mode=list prefix=/my/desired/ marker=/my/desired/0023.txt max_keys=472
# Create an empty bucket # Create an empty bucket
- s3: bucket=mybucket mode=create - s3: bucket=mybucket mode=create permission=public-read
# Create a bucket with key as directory, in the EU region # Create a bucket with key as directory, in the EU region
- s3: bucket=mybucket object=/my/directory/path mode=create region=eu-west-1 - s3: bucket=mybucket object=/my/directory/path mode=create region=eu-west-1
@ -236,6 +242,7 @@ def create_bucket(module, s3, bucket, location=None):
location = Location.DEFAULT location = Location.DEFAULT
try: try:
bucket = s3.create_bucket(bucket, location=location) bucket = s3.create_bucket(bucket, location=location)
bucket.set_acl(module.params.get('permission'))
except s3.provider.storage_response_error, e: except s3.provider.storage_response_error, e:
module.fail_json(msg= str(e)) module.fail_json(msg= str(e))
if bucket: if bucket:
@ -281,15 +288,6 @@ def create_dirkey(module, s3, bucket, obj):
except s3.provider.storage_response_error, e: except s3.provider.storage_response_error, e:
module.fail_json(msg= str(e)) module.fail_json(msg= str(e))
def upload_file_check(src):
if os.path.exists(src):
file_exists is True
else:
file_exists is False
if os.path.isdir(src):
module.fail_json(msg="Specifying a directory is not a valid source for upload.", failed=True)
return file_exists
def path_check(path): def path_check(path):
if os.path.exists(path): if os.path.exists(path):
return True return True
@ -306,6 +304,7 @@ def upload_s3file(module, s3, bucket, obj, src, expiry, metadata, encrypt, heade
key.set_metadata(meta_key, metadata[meta_key]) key.set_metadata(meta_key, metadata[meta_key])
key.set_contents_from_filename(src, encrypt_key=encrypt, headers=headers) key.set_contents_from_filename(src, encrypt_key=encrypt, headers=headers)
key.set_acl(module.params.get('permission'))
url = key.generate_url(expiry) url = key.generate_url(expiry)
module.exit_json(msg="PUT operation complete", url=url, changed=True) module.exit_json(msg="PUT operation complete", url=url, changed=True)
except s3.provider.storage_copy_error, e: except s3.provider.storage_copy_error, e:
@ -378,6 +377,7 @@ def main():
metadata = dict(type='dict'), metadata = dict(type='dict'),
mode = dict(choices=['get', 'put', 'delete', 'create', 'geturl', 'getstr', 'delobj', 'list'], required=True), mode = dict(choices=['get', 'put', 'delete', 'create', 'geturl', 'getstr', 'delobj', 'list'], required=True),
object = dict(), object = dict(),
permission = dict(choices=['private', 'public-read', 'public-read-write', 'authenticated-read'], default='private'),
version = dict(default=None), version = dict(default=None),
overwrite = dict(aliases=['force'], default='always'), overwrite = dict(aliases=['force'], default='always'),
prefix = dict(default=None), prefix = dict(default=None),

@ -268,7 +268,7 @@ options:
read_only: read_only:
description: description:
- Mount the container's root filesystem as read only - Mount the container's root filesystem as read only
default: false default: null
aliases: [] aliases: []
version_added: "2.0" version_added: "2.0"
restart_policy: restart_policy:
@ -796,12 +796,12 @@ class DockerManager(object):
'privileged': self.module.params.get('privileged'), 'privileged': self.module.params.get('privileged'),
'links': self.links, 'links': self.links,
'network_mode': self.module.params.get('net'), 'network_mode': self.module.params.get('net'),
'read_only': self.module.params.get('read_only'),
} }
optionals = {} optionals = {}
for optional_param in ('dns', 'volumes_from', 'restart_policy', for optional_param in ('dns', 'volumes_from', 'restart_policy',
'restart_policy_retry', 'pid'): 'restart_policy_retry', 'pid', 'extra_hosts', 'log_driver',
'cap_add', 'cap_drop', 'read_only'):
optionals[optional_param] = self.module.params.get(optional_param) optionals[optional_param] = self.module.params.get(optional_param)
if optionals['dns'] is not None: if optionals['dns'] is not None:
@ -818,13 +818,39 @@ class DockerManager(object):
if params['restart_policy']['Name'] == 'on-failure': if params['restart_policy']['Name'] == 'on-failure':
params['restart_policy']['MaximumRetryCount'] = optionals['restart_policy_retry'] params['restart_policy']['MaximumRetryCount'] = optionals['restart_policy_retry']
# docker_py only accepts 'host' or None
if 'pid' in optionals and not optionals['pid']:
optionals['pid'] = None
if optionals['pid'] is not None: if optionals['pid'] is not None:
self.ensure_capability('pid') self.ensure_capability('pid')
params['pid_mode'] = optionals['pid'] params['pid_mode'] = optionals['pid']
if optionals['extra_hosts'] is not None:
self.ensure_capability('extra_hosts')
params['extra_hosts'] = optionals['extra_hosts']
if optionals['log_driver'] is not None:
self.ensure_capability('log_driver')
log_config = docker.utils.LogConfig(type=docker.utils.LogConfig.types.JSON)
log_config.type = optionals['log_driver']
params['log_config'] = log_config
if optionals['cap_add'] is not None:
self.ensure_capability('cap_add')
params['cap_add'] = optionals['cap_add']
if optionals['cap_drop'] is not None:
self.ensure_capability('cap_drop')
params['cap_drop'] = optionals['cap_drop']
if optionals['read_only'] is not None:
self.ensure_capability('read_only')
params['read_only'] = optionals['read_only']
return params return params
def get_host_config(self): def create_host_config(self):
""" """
Create HostConfig object Create HostConfig object
""" """
@ -1020,7 +1046,7 @@ class DockerManager(object):
expected_volume_keys = set((image['ContainerConfig']['Volumes'] or {}).keys()) expected_volume_keys = set((image['ContainerConfig']['Volumes'] or {}).keys())
if self.volumes: if self.volumes:
expected_volume_keys.update(self.volumes.keys()) expected_volume_keys.update(self.volumes)
actual_volume_keys = set((container['Config']['Volumes'] or {}).keys()) actual_volume_keys = set((container['Config']['Volumes'] or {}).keys())
@ -1340,65 +1366,6 @@ class DockerManager(object):
except Exception as e: except Exception as e:
self.module.fail_json(msg="Failed to pull the specified image: %s" % resource, error=repr(e)) self.module.fail_json(msg="Failed to pull the specified image: %s" % resource, error=repr(e))
def create_host_config(self):
params = {
'lxc_conf': self.lxc_conf,
'binds': self.binds,
'port_bindings': self.port_bindings,
'publish_all_ports': self.module.params.get('publish_all_ports'),
'privileged': self.module.params.get('privileged'),
'links': self.links,
'network_mode': self.module.params.get('net'),
}
optionals = {}
for optional_param in ('dns', 'volumes_from', 'restart_policy',
'restart_policy_retry', 'pid', 'extra_hosts', 'log_driver',
'cap_add', 'cap_drop'):
optionals[optional_param] = self.module.params.get(optional_param)
if optionals['dns'] is not None:
self.ensure_capability('dns')
params['dns'] = optionals['dns']
if optionals['volumes_from'] is not None:
self.ensure_capability('volumes_from')
params['volumes_from'] = optionals['volumes_from']
if optionals['restart_policy'] is not None:
self.ensure_capability('restart_policy')
params['restart_policy'] = { 'Name': optionals['restart_policy'] }
if params['restart_policy']['Name'] == 'on-failure':
params['restart_policy']['MaximumRetryCount'] = optionals['restart_policy_retry']
# docker_py only accepts 'host' or None
if 'pid' in optionals and not optionals['pid']:
optionals['pid'] = None
if optionals['pid'] is not None:
self.ensure_capability('pid')
params['pid_mode'] = optionals['pid']
if optionals['extra_hosts'] is not None:
self.ensure_capability('extra_hosts')
params['extra_hosts'] = optionals['extra_hosts']
if optionals['log_driver'] is not None:
self.ensure_capability('log_driver')
log_config = docker.utils.LogConfig(type=docker.utils.LogConfig.types.JSON)
log_config.type = optionals['log_driver']
params['log_config'] = log_config
if optionals['cap_add'] is not None:
self.ensure_capability('cap_add')
params['cap_add'] = optionals['cap_add']
if optionals['cap_drop'] is not None:
self.ensure_capability('cap_drop')
params['cap_drop'] = optionals['cap_drop']
return docker.utils.create_host_config(**params)
def create_containers(self, count=1): def create_containers(self, count=1):
try: try:
mem_limit = _human_to_bytes(self.module.params.get('memory_limit')) mem_limit = _human_to_bytes(self.module.params.get('memory_limit'))
@ -1418,17 +1385,16 @@ class DockerManager(object):
'stdin_open': self.module.params.get('stdin_open'), 'stdin_open': self.module.params.get('stdin_open'),
'tty': self.module.params.get('tty'), 'tty': self.module.params.get('tty'),
'cpuset': self.module.params.get('cpu_set'), 'cpuset': self.module.params.get('cpu_set'),
'host_config': self.create_host_config(),
'user': self.module.params.get('docker_user'), 'user': self.module.params.get('docker_user'),
} }
if self.ensure_capability('host_config', fail=False): if self.ensure_capability('host_config', fail=False):
params['host_config'] = self.get_host_config() params['host_config'] = self.create_host_config()
#For v1.19 API and above use HostConfig, otherwise use Config #For v1.19 API and above use HostConfig, otherwise use Config
if api_version < 1.19: if api_version < 1.19:
params['mem_limit'] = mem_limit params['mem_limit'] = mem_limit
else: else:
params['host_config']['mem_limit'] = mem_limit params['host_config']['Memory'] = mem_limit
def do_create(count, params): def do_create(count, params):
@ -1664,7 +1630,7 @@ def main():
cpu_set = dict(default=None), cpu_set = dict(default=None),
cap_add = dict(default=None, type='list'), cap_add = dict(default=None, type='list'),
cap_drop = dict(default=None, type='list'), cap_drop = dict(default=None, type='list'),
read_only = dict(default=False, type='bool'), read_only = dict(default=None, type='bool'),
), ),
required_together = ( required_together = (
['tls_client_cert', 'tls_client_key'], ['tls_client_cert', 'tls_client_key'],

@ -211,15 +211,6 @@ def create_dirkey(module, gs, bucket, obj):
except gs.provider.storage_response_error, e: except gs.provider.storage_response_error, e:
module.fail_json(msg= str(e)) module.fail_json(msg= str(e))
def upload_file_check(src):
if os.path.exists(src):
file_exists is True
else:
file_exists is False
if os.path.isdir(src):
module.fail_json(msg="Specifying a directory is not a valid source for upload.", failed=True)
return file_exists
def path_check(path): def path_check(path):
if os.path.exists(path): if os.path.exists(path):
return True return True

@ -252,8 +252,20 @@ def ensure_user_exists(keystone, user_name, password, email, tenant_name,
email=email, tenant_id=tenant.id) email=email, tenant_id=tenant.id)
return (True, user.id) return (True, user.id)
def ensure_role_exists(keystone, role_name):
# Get the role if it exists
try:
role = get_role(keystone, role_name)
# Role does exist, we're done
return (False, role.id)
except KeyError:
# Role doesn't exist yet
pass
role = keystone.roles.create(role_name)
return (True, role.id)
def ensure_role_exists(keystone, user_name, tenant_name, role_name, def ensure_user_role_exists(keystone, user_name, tenant_name, role_name,
check_mode): check_mode):
""" Check if role exists """ Check if role exists
@ -297,9 +309,11 @@ def ensure_user_absent(keystone, user, check_mode):
raise NotImplementedError("Not yet implemented") raise NotImplementedError("Not yet implemented")
def ensure_role_absent(keystone, uesr, tenant, role, check_mode): def ensure_user_role_absent(keystone, uesr, tenant, role, check_mode):
raise NotImplementedError("Not yet implemented") raise NotImplementedError("Not yet implemented")
def ensure_role_absent(keystone, role_name):
raise NotImplementedError("Not yet implemented")
def main(): def main():
@ -378,14 +392,18 @@ def dispatch(keystone, user=None, password=None, tenant=None,
X absent ensure_tenant_absent X absent ensure_tenant_absent
X X present ensure_user_exists X X present ensure_user_exists
X X absent ensure_user_absent X X absent ensure_user_absent
X X X present ensure_role_exists X X X present ensure_user_role_exists
X X X absent ensure_role_absent X X X absent ensure_user_role_absent
X present ensure_role_exists
X absent ensure_role_absent
""" """
changed = False changed = False
id = None id = None
if tenant and not user and not role and state == "present": if not tenant and not user and role and state == "present":
changed, id = ensure_role_exists(keystone, role)
elif not tenant and not user and role and state == "absent":
changed = ensure_role_absent(keystone, role)
elif tenant and not user and not role and state == "present":
changed, id = ensure_tenant_exists(keystone, tenant, changed, id = ensure_tenant_exists(keystone, tenant,
tenant_description, check_mode) tenant_description, check_mode)
elif tenant and not user and not role and state == "absent": elif tenant and not user and not role and state == "absent":
@ -396,10 +414,10 @@ def dispatch(keystone, user=None, password=None, tenant=None,
elif tenant and user and not role and state == "absent": elif tenant and user and not role and state == "absent":
changed = ensure_user_absent(keystone, user, check_mode) changed = ensure_user_absent(keystone, user, check_mode)
elif tenant and user and role and state == "present": elif tenant and user and role and state == "present":
changed, id = ensure_role_exists(keystone, user, tenant, role, changed, id = ensure_user_role_exists(keystone, user, tenant, role,
check_mode) check_mode)
elif tenant and user and role and state == "absent": elif tenant and user and role and state == "absent":
changed = ensure_role_absent(keystone, user, tenant, role, check_mode) changed = ensure_user_role_absent(keystone, user, tenant, role, check_mode)
else: else:
# Should never reach here # Should never reach here
raise ValueError("Code should never reach here") raise ValueError("Code should never reach here")

@ -40,12 +40,12 @@ author: "Monty Taylor (@emonty)"
EXAMPLES = ''' EXAMPLES = '''
# Get list of clouds that do not support security groups # Get list of clouds that do not support security groups
- os-client-config: - os_client_config:
- debug: var={{ item }} - debug: var={{ item }}
with_items: "{{ openstack.clouds|rejectattr('secgroup_source', 'none')|list() }}" with_items: "{{ openstack.clouds|rejectattr('secgroup_source', 'none')|list() }}"
# Get the information back just about the mordred cloud # Get the information back just about the mordred cloud
- os-client-config: - os_client_config:
clouds: clouds:
- mordred - mordred
''' '''

@ -57,8 +57,13 @@ requirements: ["shade"]
EXAMPLES = ''' EXAMPLES = '''
- os_network: - os_network:
name=t1network name: t1network
state=present state: present
auth:
auth_url: https://your_api_url.com:9000/v2.0
username: user
password: password
project_name: someproject
''' '''

@ -213,12 +213,14 @@ def _find_matching_rule(module, secgroup):
remote_ip_prefix = module.params['remote_ip_prefix'] remote_ip_prefix = module.params['remote_ip_prefix']
ethertype = module.params['ethertype'] ethertype = module.params['ethertype']
direction = module.params['direction'] direction = module.params['direction']
remote_group_id = module.params['remote_group']
for rule in secgroup['security_group_rules']: for rule in secgroup['security_group_rules']:
if (protocol == rule['protocol'] if (protocol == rule['protocol']
and remote_ip_prefix == rule['remote_ip_prefix'] and remote_ip_prefix == rule['remote_ip_prefix']
and ethertype == rule['ethertype'] and ethertype == rule['ethertype']
and direction == rule['direction'] and direction == rule['direction']
and remote_group_id == rule['remote_group_id']
and _ports_match(protocol, and _ports_match(protocol,
module.params['port_range_min'], module.params['port_range_min'],
module.params['port_range_max'], module.params['port_range_max'],

@ -287,8 +287,12 @@ def _create_server(module, cloud):
if flavor: if flavor:
flavor_dict = cloud.get_flavor(flavor) flavor_dict = cloud.get_flavor(flavor)
if not flavor_dict:
module.fail_json(msg="Could not find flavor %s" % flavor)
else: else:
flavor_dict = cloud.get_flavor_by_ram(flavor_ram, flavor_include) flavor_dict = cloud.get_flavor_by_ram(flavor_ram, flavor_include)
if not flavor_dict:
module.fail_json(msg="Could not find any matching flavor")
nics = _network_args(module, cloud) nics = _network_args(module, cloud)
@ -392,7 +396,7 @@ def main():
flavor_include = dict(default=None), flavor_include = dict(default=None),
key_name = dict(default=None), key_name = dict(default=None),
security_groups = dict(default='default'), security_groups = dict(default='default'),
nics = dict(default=[]), nics = dict(default=[], type='list'),
meta = dict(default=None), meta = dict(default=None),
userdata = dict(default=None), userdata = dict(default=None),
config_drive = dict(default=False, type='bool'), config_drive = dict(default=False, type='bool'),

@ -97,7 +97,9 @@ def rax_facts(module, address, name, server_id):
servers.append(cs.servers.get(server_id)) servers.append(cs.servers.get(server_id))
except Exception, e: except Exception, e:
pass pass
servers[:] = [server for server in servers if server.status != "DELETED"]
if len(servers) > 1: if len(servers) > 1:
module.fail_json(msg='Multiple servers found matching provided ' module.fail_json(msg='Multiple servers found matching provided '
'search parameters') 'search parameters')

@ -152,6 +152,7 @@ EXAMPLES = '''
# Returns changed = True and a adds ansible_facts from the new VM # Returns changed = True and a adds ansible_facts from the new VM
# State will set the power status of a guest upon creation. Use powered_on to create and boot. # State will set the power status of a guest upon creation. Use powered_on to create and boot.
# Options ['state', 'vm_extra_config', 'vm_disk', 'vm_nic', 'vm_hardware', 'esxi'] are required together # Options ['state', 'vm_extra_config', 'vm_disk', 'vm_nic', 'vm_hardware', 'esxi'] are required together
# Note: vm_floppy support added in 2.0
- vsphere_guest: - vsphere_guest:
vcenter_hostname: vcenter.mydomain.local vcenter_hostname: vcenter.mydomain.local
@ -185,6 +186,9 @@ EXAMPLES = '''
vm_cdrom: vm_cdrom:
type: "iso" type: "iso"
iso_path: "DatastoreName/cd-image.iso" iso_path: "DatastoreName/cd-image.iso"
vm_floppy:
type: "image"
image_path: "DatastoreName/floppy-image.flp"
esxi: esxi:
datacenter: MyDatacenter datacenter: MyDatacenter
hostname: esx001.mydomain.local hostname: esx001.mydomain.local
@ -376,6 +380,44 @@ def add_cdrom(module, s, config_target, config, devices, default_devs, type="cli
devices.append(cd_spec) devices.append(cd_spec)
def add_floppy(module, s, config_target, config, devices, default_devs, type="image", vm_floppy_image_path=None):
# Add a floppy
# Make sure the datastore exists.
if vm_floppy_image_path:
image_location = vm_floppy_image_path.split('/', 1)
datastore, ds = find_datastore(
module, s, image_location[0], config_target)
image_path = image_location[1]
floppy_spec = config.new_deviceChange()
floppy_spec.set_element_operation('add')
floppy_ctrl = VI.ns0.VirtualFloppy_Def("floppy_ctrl").pyclass()
if type == "image":
image = VI.ns0.VirtualFloppyImageBackingInfo_Def("image").pyclass()
ds_ref = image.new_datastore(ds)
ds_ref.set_attribute_type(ds.get_attribute_type())
image.set_element_datastore(ds_ref)
image.set_element_fileName("%s %s" % (datastore, image_path))
floppy_ctrl.set_element_backing(image)
floppy_ctrl.set_element_key(3)
floppy_spec.set_element_device(floppy_ctrl)
elif type == "client":
client = VI.ns0.VirtualFloppyRemoteDeviceBackingInfo_Def(
"client").pyclass()
client.set_element_deviceName("/dev/fd0")
floppy_ctrl.set_element_backing(client)
floppy_ctrl.set_element_key(3)
floppy_spec.set_element_device(floppy_ctrl)
else:
s.disconnect()
module.fail_json(
msg="Error adding floppy of type %s to vm spec. "
" floppy type can either be image or client" % (type))
devices.append(floppy_spec)
def add_nic(module, s, nfmor, config, devices, nic_type="vmxnet3", network_name="VM Network", network_type="standard"): def add_nic(module, s, nfmor, config, devices, nic_type="vmxnet3", network_name="VM Network", network_type="standard"):
# add a NIC # add a NIC
# Different network card types are: "VirtualE1000", # Different network card types are: "VirtualE1000",
@ -946,6 +988,27 @@ def create_vm(vsphere_client, module, esxi, resource_pool, cluster_name, guest,
# Add a CD-ROM device to the VM. # Add a CD-ROM device to the VM.
add_cdrom(module, vsphere_client, config_target, config, devices, add_cdrom(module, vsphere_client, config_target, config, devices,
default_devs, cdrom_type, cdrom_iso_path) default_devs, cdrom_type, cdrom_iso_path)
if 'vm_floppy' in vm_hardware:
floppy_image_path = None
floppy_type = None
try:
floppy_type = vm_hardware['vm_floppy']['type']
except KeyError:
vsphere_client.disconnect()
module.fail_json(
msg="Error on %s definition. floppy type needs to be"
" specified." % vm_hardware['vm_floppy'])
if floppy_type == 'image':
try:
floppy_image_path = vm_hardware['vm_floppy']['image_path']
except KeyError:
vsphere_client.disconnect()
module.fail_json(
msg="Error on %s definition. floppy image_path needs"
" to be specified." % vm_hardware['vm_floppy'])
# Add a floppy to the VM.
add_floppy(module, vsphere_client, config_target, config, devices,
default_devs, floppy_type, floppy_image_path)
if vm_nic: if vm_nic:
for nic in sorted(vm_nic.iterkeys()): for nic in sorted(vm_nic.iterkeys()):
try: try:

@ -91,9 +91,11 @@ options:
validate is passed in via '%s' which must be present as in the sshd example below. validate is passed in via '%s' which must be present as in the sshd example below.
The command is passed securely so shell features like expansion and pipes won't work. The command is passed securely so shell features like expansion and pipes won't work.
required: false required: false
default: "" default: null
version_added: "2.0"
author: "Stephen Fromm (@sfromm)" author: "Stephen Fromm (@sfromm)"
extends_documentation_fragment: files extends_documentation_fragment:
- files
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -104,7 +106,7 @@ EXAMPLES = '''
- assemble: src=/etc/someapp/fragments dest=/etc/someapp/someapp.conf delimiter='### START FRAGMENT ###' - assemble: src=/etc/someapp/fragments dest=/etc/someapp/someapp.conf delimiter='### START FRAGMENT ###'
# Copy a new "sshd_config" file into place, after passing validation with sshd # Copy a new "sshd_config" file into place, after passing validation with sshd
- assemble: src=/etc/ssh/conf.d/ dest=/etc/ssh/sshd_config validate='sshd -t -f %s' - assemble: src=/etc/ssh/conf.d/ dest=/etc/ssh/sshd_config validate='/usr/sbin/sshd -t -f %s'
''' '''
# =========================================== # ===========================================

@ -179,8 +179,8 @@ APT_ENV_VARS = dict(
) )
DPKG_OPTIONS = 'force-confdef,force-confold' DPKG_OPTIONS = 'force-confdef,force-confold'
APT_GET_ZERO = "0 upgraded, 0 newly installed" APT_GET_ZERO = "\n0 upgraded, 0 newly installed"
APTITUDE_ZERO = "0 packages upgraded, 0 newly installed" APTITUDE_ZERO = "\n0 packages upgraded, 0 newly installed"
APT_LISTS_PATH = "/var/lib/apt/lists" APT_LISTS_PATH = "/var/lib/apt/lists"
APT_UPDATE_SUCCESS_STAMP_PATH = "/var/lib/apt/periodic/update-success-stamp" APT_UPDATE_SUCCESS_STAMP_PATH = "/var/lib/apt/periodic/update-success-stamp"

@ -76,6 +76,12 @@ EXAMPLES = '''
- redhat_subscription: state=present - redhat_subscription: state=present
activationkey=1-222333444 activationkey=1-222333444
pool='^(Red Hat Enterprise Server|Red Hat Virtualization)$' pool='^(Red Hat Enterprise Server|Red Hat Virtualization)$'
# Update the consumed subscriptions from the previous example (remove the Red
# Hat Virtualization subscription)
- redhat_subscription: state=present
activationkey=1-222333444
pool='^Red Hat Enterprise Server$'
''' '''
import os import os
@ -180,7 +186,7 @@ class Rhsm(RegistrationBase):
for k,v in kwargs.items(): for k,v in kwargs.items():
if re.search(r'^(system|rhsm)_', k): if re.search(r'^(system|rhsm)_', k):
args.append('--%s=%s' % (k.replace('_','.'), v)) args.append('--%s=%s' % (k.replace('_','.'), v))
self.module.run_command(args, check_rc=True) self.module.run_command(args, check_rc=True)
@property @property
@ -226,14 +232,26 @@ class Rhsm(RegistrationBase):
rc, stderr, stdout = self.module.run_command(args, check_rc=True) rc, stderr, stdout = self.module.run_command(args, check_rc=True)
def unsubscribe(self): def unsubscribe(self, serials=None):
''' '''
Unsubscribe a system from all subscribed channels Unsubscribe a system from subscribed channels
Args:
serials(list or None): list of serials to unsubscribe. If
serials is none or an empty list, then
all subscribed channels will be removed.
Raises: Raises:
* Exception - if error occurs while running command * Exception - if error occurs while running command
''' '''
args = ['subscription-manager', 'unsubscribe', '--all'] items = []
rc, stderr, stdout = self.module.run_command(args, check_rc=True) if serials is not None and serials:
items = ["--serial=%s" % s for s in serials]
if serials is None:
items = ["--all"]
if items:
args = ['subscription-manager', 'unsubscribe'] + items
rc, stderr, stdout = self.module.run_command(args, check_rc=True)
return serials
def unregister(self): def unregister(self):
''' '''
@ -255,8 +273,27 @@ class Rhsm(RegistrationBase):
# Available pools ready for subscription # Available pools ready for subscription
available_pools = RhsmPools(self.module) available_pools = RhsmPools(self.module)
subscribed_pool_ids = []
for pool in available_pools.filter(regexp): for pool in available_pools.filter(regexp):
pool.subscribe() pool.subscribe()
subscribed_pool_ids.append(pool.get_pool_id())
return subscribed_pool_ids
def update_subscriptions(self, regexp):
changed=False
consumed_pools = RhsmPools(self.module, consumed=True)
pool_ids_to_keep = [p.get_pool_id() for p in consumed_pools.filter(regexp)]
serials_to_remove=[p.Serial for p in consumed_pools if p.get_pool_id() not in pool_ids_to_keep]
serials = self.unsubscribe(serials=serials_to_remove)
subscribed_pool_ids = self.subscribe(regexp)
if subscribed_pool_ids or serials:
changed=True
return {'changed': changed, 'subscribed_pool_ids': subscribed_pool_ids,
'unsubscribed_serials': serials}
class RhsmPool(object): class RhsmPool(object):
@ -272,8 +309,11 @@ class RhsmPool(object):
def __str__(self): def __str__(self):
return str(self.__getattribute__('_name')) return str(self.__getattribute__('_name'))
def get_pool_id(self):
return getattr(self, 'PoolId', getattr(self, 'PoolID'))
def subscribe(self): def subscribe(self):
args = "subscription-manager subscribe --pool %s" % self.PoolId args = "subscription-manager subscribe --pool %s" % self.get_pool_id()
rc, stdout, stderr = self.module.run_command(args, check_rc=True) rc, stdout, stderr = self.module.run_command(args, check_rc=True)
if rc == 0: if rc == 0:
return True return True
@ -285,18 +325,25 @@ class RhsmPools(object):
""" """
This class is used for manipulating pools subscriptions with RHSM This class is used for manipulating pools subscriptions with RHSM
""" """
def __init__(self, module): def __init__(self, module, consumed=False):
self.module = module self.module = module
self.products = self._load_product_list() self.products = self._load_product_list(consumed)
def __iter__(self): def __iter__(self):
return self.products.__iter__() return self.products.__iter__()
def _load_product_list(self): def _load_product_list(self, consumed=False):
""" """
Loads list of all available pools for system in data structure Loads list of all available or consumed pools for system in data structure
Args:
consumed(bool): if True list consumed pools, else list available pools (default False)
""" """
args = "subscription-manager list --available" args = "subscription-manager list"
if consumed:
args += " --consumed"
else:
args += " --available"
rc, stdout, stderr = self.module.run_command(args, check_rc=True) rc, stdout, stderr = self.module.run_command(args, check_rc=True)
products = [] products = []
@ -375,18 +422,27 @@ def main():
# Register system # Register system
if rhn.is_registered: if rhn.is_registered:
module.exit_json(changed=False, msg="System already registered.") if pool != '^$':
try:
result = rhn.update_subscriptions(pool)
except Exception, e:
module.fail_json(msg="Failed to update subscriptions for '%s': %s" % (server_hostname, e))
else:
module.exit_json(**result)
else:
module.exit_json(changed=False, msg="System already registered.")
else: else:
try: try:
rhn.enable() rhn.enable()
rhn.configure(**module.params) rhn.configure(**module.params)
rhn.register(username, password, autosubscribe, activationkey, org_id) rhn.register(username, password, autosubscribe, activationkey, org_id)
rhn.subscribe(pool) subscribed_pool_ids = rhn.subscribe(pool)
except Exception, e: except Exception, e:
module.fail_json(msg="Failed to register with '%s': %s" % (server_hostname, e)) module.fail_json(msg="Failed to register with '%s': %s" % (server_hostname, e))
else: else:
module.exit_json(changed=True, msg="System successfully registered to '%s'." % server_hostname) module.exit_json(changed=True,
msg="System successfully registered to '%s'." % server_hostname,
subscribed_pool_ids=subscribed_pool_ids)
# Ensure system is *not* registered # Ensure system is *not* registered
if state == 'absent': if state == 'absent':
if not rhn.is_registered: if not rhn.is_registered:

@ -224,7 +224,7 @@ def is_installed(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=None, di
for rid in en_repos: for rid in en_repos:
my.repos.enableRepo(rid) my.repos.enableRepo(rid)
e,m,u = my.rpmdb.matchPackageNames([pkgspec]) e, m, u = my.rpmdb.matchPackageNames([pkgspec])
pkgs = e + m pkgs = e + m
if not pkgs: if not pkgs:
pkgs.extend(my.returnInstalledPackagesByDep(pkgspec)) pkgs.extend(my.returnInstalledPackagesByDep(pkgspec))
@ -236,16 +236,16 @@ def is_installed(module, repoq, pkgspec, conf_file, qf=def_qf, en_repos=None, di
else: else:
cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, pkgspec] cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, pkgspec]
rc,out,err = module.run_command(cmd) rc, out, err = module.run_command(cmd)
if not is_pkg: if not is_pkg:
cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, "--whatprovides", pkgspec] cmd = repoq + ["--disablerepo=*", "--pkgnarrow=installed", "--qf", qf, "--whatprovides", pkgspec]
rc2,out2,err2 = module.run_command(cmd) rc2, out2, err2 = module.run_command(cmd)
else: else:
rc2,out2,err2 = (0, '', '') rc2, out2, err2 = (0, '', '')
if rc == 0 and rc2 == 0: if rc == 0 and rc2 == 0:
out += out2 out += out2
return [ p for p in out.split('\n') if p.strip() ] return [p for p in out.split('\n') if p.strip()]
else: else:
module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2)) module.fail_json(msg='Error from repoquery: %s: %s' % (cmd, err + err2))
@ -553,7 +553,7 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
module.fail_json(msg="Failure downloading %s, %s" % (spec, e)) module.fail_json(msg="Failure downloading %s, %s" % (spec, e))
#groups :( #groups :(
elif spec.startswith('@'): elif spec.startswith('@'):
# complete wild ass guess b/c it's a group # complete wild ass guess b/c it's a group
pkg = spec pkg = spec
@ -620,7 +620,8 @@ def install(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
shutil.rmtree(tempdir) shutil.rmtree(tempdir)
except Exception, e: except Exception, e:
module.fail_json(msg="Failure deleting temp directory %s, %s" % (tempdir, e)) module.fail_json(msg="Failure deleting temp directory %s, %s" % (tempdir, e))
module.exit_json(changed=True)
module.exit_json(changed=True, results=res['results'], changes=dict(installed=pkgs))
changed = True changed = True
@ -688,7 +689,7 @@ def remove(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
cmd = yum_basecmd + ["remove"] + pkgs cmd = yum_basecmd + ["remove"] + pkgs
if module.check_mode: if module.check_mode:
module.exit_json(changed=True) module.exit_json(changed=True, results=res['results'], changes=dict(removed=pkgs))
rc, out, err = module.run_command(cmd) rc, out, err = module.run_command(cmd)
@ -723,47 +724,69 @@ def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
res['msg'] = '' res['msg'] = ''
res['changed'] = False res['changed'] = False
res['rc'] = 0 res['rc'] = 0
pkgs = {}
for spec in items: pkgs['update'] = []
pkgs['install'] = []
pkg = None updates = {}
basecmd = 'update' update_all = False
cmd = '' cmd = None
# groups, again
if spec.startswith('@'): # determine if we're doing an update all
pkg = spec if '*' in items:
update_all = True
elif spec == '*': #update all
# use check-update to see if there is any need # run check-update to see if we have packages pending
rc,out,err = module.run_command(yum_basecmd + ['check-update']) rc, out, err = module.run_command(yum_basecmd + ['check-update'])
if rc == 100: if rc == 0 and update_all:
cmd = yum_basecmd + [basecmd] res['results'].append('Nothing to do here, all packages are up to date')
else: return res
res['results'].append('All packages up to date') elif rc == 100:
available_updates = out.split('\n')
# build update dictionary
for line in available_updates:
line = line.split()
# ignore irrelevant lines
# FIXME... revisit for something less kludgy
if '*' in line or len(line) != 3 or '.' not in line[0]:
continue continue
# dep/pkgname - find it
else:
if is_installed(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos):
basecmd = 'update'
else: else:
basecmd = 'install' pkg, version, repo = line
name, dist = pkg.rsplit('.', 1)
updates.update({name: {'version': version, 'dist': dist, 'repo': repo}})
elif rc == 1:
res['msg'] = err
res['rc'] = rc
module.fail_json(**res)
if update_all:
cmd = yum_basecmd + ['update']
else:
for spec in items:
# some guess work involved with groups. update @<group> will install the group if missing
if spec.startswith('@'):
pkgs['update'].append(spec)
continue
# dep/pkgname - find it
else:
if is_installed(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos):
pkgs['update'].append(spec)
else:
pkgs['install'].append(spec)
pkglist = what_provides(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos) pkglist = what_provides(module, repoq, spec, conf_file, en_repos=en_repos, dis_repos=dis_repos)
# FIXME..? may not be desirable to throw an exception here if a single package is missing
if not pkglist: if not pkglist:
res['msg'] += "No Package matching '%s' found available, installed or updated" % spec res['msg'] += "No Package matching '%s' found available, installed or updated" % spec
module.fail_json(**res) module.fail_json(**res)
nothing_to_do = True nothing_to_do = True
for this in pkglist: for this in pkglist:
if basecmd == 'install' and is_available(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos): if spec in pkgs['install'] and is_available(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos):
nothing_to_do = False
break
if basecmd == 'update' and is_update(module, repoq, this, conf_file, en_repos=en_repos, dis_repos=dis_repos):
nothing_to_do = False nothing_to_do = False
break break
if spec in pkgs['update'] and spec in updates.keys():
nothing_to_do = False
if nothing_to_do: if nothing_to_do:
res['results'].append("All packages providing %s are up to date" % spec) res['results'].append("All packages providing %s are up to date" % spec)
continue continue
@ -775,26 +798,59 @@ def latest(module, items, repoq, yum_basecmd, conf_file, en_repos, dis_repos):
res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts) res['msg'] += "The following packages have pending transactions: %s" % ", ".join(conflicts)
module.fail_json(**res) module.fail_json(**res)
pkg = spec # list of package updates
if not cmd: if update_all:
cmd = yum_basecmd + [basecmd, pkg] will_update = updates.keys()
else:
if module.check_mode: will_update = [u for u in pkgs['update'] if u in updates.keys() or u.startswith('@')]
return module.exit_json(changed=True)
# check_mode output
if module.check_mode:
to_update = []
for w in will_update:
if w.startswith('@'):
to_update.append((w, None))
msg = '%s will be updated' % w
else:
to_update.append((w, '%s.%s from %s' % (updates[w]['version'], updates[w]['dist'], updates[w]['repo'])))
rc, out, err = module.run_command(cmd) res['changes'] = dict(installed=pkgs['install'], updated=to_update)
res['rc'] += rc if len(will_update) > 0 or len(pkgs['install']) > 0:
res['results'].append(out) res['changed'] = True
res['msg'] += err
# FIXME if it is - update it and check to see if it applied return res
# check to see if there is no longer an update available for the pkgspec
if rc: # run commands
res['failed'] = True if cmd: # update all
rc, out, err = module.run_command(cmd)
res['changed'] = True
else:
if len(pkgs['install']) > 0: # install missing
cmd = yum_basecmd + ['install'] + pkgs['install']
rc, out, err = module.run_command(cmd)
res['changed'] = True
else: else:
rc, out, err = [0, '', '']
if len(will_update) > 0: # update present
cmd = yum_basecmd + ['update'] + pkgs['update']
rc2, out2, err2 = module.run_command(cmd)
res['changed'] = True res['changed'] = True
else:
rc2, out2, err2 = [0, '', '']
if not update_all:
rc += rc2
out += out2
err += err2
res['rc'] += rc
res['msg'] += err
res['results'].append(out)
if rc:
res['failed'] = True
return res return res
@ -916,10 +972,15 @@ def main():
# loaded and plugins are discovered # loaded and plugins are discovered
my.conf my.conf
repoquery = None repoquery = None
if 'rhnplugin' in my.plugins._plugins: try:
repoquerybin = ensure_yum_utils(module) yum_plugins = my.plugins._plugins
if repoquerybin: except AttributeError:
repoquery = [repoquerybin, '--show-duplicates', '--plugins', '--quiet'] pass
else:
if 'rhnplugin' in yum_plugins:
repoquerybin = ensure_yum_utils(module)
if repoquerybin:
repoquery = [repoquerybin, '--show-duplicates', '--plugins', '--quiet']
pkg = [ p.strip() for p in params['name']] pkg = [ p.strip() for p in params['name']]
exclude = params['exclude'] exclude = params['exclude']
@ -939,4 +1000,3 @@ from ansible.module_utils.basic import *
from ansible.module_utils.urls import * from ansible.module_utils.urls import *
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -491,10 +491,20 @@ def get_head_branch(git_path, module, dest, remote, bare=False):
f.close() f.close()
return branch return branch
def fetch(git_path, module, repo, dest, version, remote, bare, refspec): def set_remote_url(git_path, module, repo, dest, remote):
''' updates repo from remote sources ''' ''' updates repo from remote sources '''
commands = [("set a new url %s for %s" % (repo, remote), [git_path, 'remote', 'set-url', remote, repo])] commands = [("set a new url %s for %s" % (repo, remote), [git_path, 'remote', 'set-url', remote, repo])]
for (label,command) in commands:
(rc,out,err) = module.run_command(command, cwd=dest)
if rc != 0:
module.fail_json(msg="Failed to %s: %s %s" % (label, out, err))
def fetch(git_path, module, repo, dest, version, remote, bare, refspec):
''' updates repo from remote sources '''
set_remote_url(git_path, module, repo, dest, remote)
commands = []
fetch_str = 'download remote objects and refs' fetch_str = 'download remote objects and refs'
if bare: if bare:
@ -741,6 +751,7 @@ def main():
if not module.check_mode: if not module.check_mode:
reset(git_path, module, dest) reset(git_path, module, dest)
# exit if already at desired sha version # exit if already at desired sha version
set_remote_url(git_path, module, repo, dest, remote)
remote_head = get_remote_head(git_path, module, dest, version, remote, bare) remote_head = get_remote_head(git_path, module, dest, version, remote, bare)
if before == remote_head: if before == remote_head:
if local_mods: if local_mods:

@ -112,8 +112,10 @@ EXAMPLES = '''
key_options='no-port-forwarding,from="10.0.1.1"' key_options='no-port-forwarding,from="10.0.1.1"'
# Set up authorized_keys exclusively with one key # Set up authorized_keys exclusively with one key
- authorized_key: user=root key=public_keys/doe-jane state=present - authorized_key: user=root key="{{ item }}" state=present
exclusive=yes exclusive=yes
with_file:
- public_keys/doe-jane
''' '''
# Makes sure the public key line is present or absent in the user's .ssh/authorized_keys. # Makes sure the public key line is present or absent in the user's .ssh/authorized_keys.
@ -169,16 +171,15 @@ def keyfile(module, user, write=False, path=None, manage_dir=True):
:return: full path string to authorized_keys for user :return: full path string to authorized_keys for user
""" """
if module.check_mode: if module.check_mode and path is not None:
if path is None: keysfile = path
module.fail_json(msg="You must provide full path to key file in check mode") return keysfile
else:
keysfile = path
return keysfile
try: try:
user_entry = pwd.getpwnam(user) user_entry = pwd.getpwnam(user)
except KeyError, e: except KeyError, e:
if module.check_mode and path is None:
module.fail_json(msg="Either user must exist or you must provide full path to key file in check mode")
module.fail_json(msg="Failed to lookup user %s: %s" % (user, str(e))) module.fail_json(msg="Failed to lookup user %s: %s" % (user, str(e)))
if path is None: if path is None:
homedir = user_entry.pw_dir homedir = user_entry.pw_dir

@ -491,6 +491,11 @@ class DebianHostname(Hostname):
distribution = 'Debian' distribution = 'Debian'
strategy_class = DebianStrategy strategy_class = DebianStrategy
class KaliHostname(Hostname):
platform = 'Linux'
distribution = 'Kali'
strategy_class = DebianStrategy
class UbuntuHostname(Hostname): class UbuntuHostname(Hostname):
platform = 'Linux' platform = 'Linux'
distribution = 'Ubuntu' distribution = 'Ubuntu'

@ -520,9 +520,14 @@ class LinuxService(Service):
return False return False
def get_systemd_status_dict(self): def get_systemd_status_dict(self):
(rc, out, err) = self.execute_command("%s show %s" % (self.enable_cmd, self.__systemd_unit,))
# Check status first as show will not fail if service does not exist
(rc, out, err) = self.execute_command("%s show '%s'" % (self.enable_cmd, self.__systemd_unit,))
if rc != 0: if rc != 0:
self.module.fail_json(msg='failure %d running systemctl show for %r: %s' % (rc, self.__systemd_unit, err)) self.module.fail_json(msg='failure %d running systemctl show for %r: %s' % (rc, self.__systemd_unit, err))
elif 'LoadState=not-found' in out:
self.module.fail_json(msg='systemd could not find the requested service "%r": %s' % (self.__systemd_unit, err))
key = None key = None
value_buffer = [] value_buffer = []
status_dict = {} status_dict = {}

@ -585,11 +585,13 @@ class User(object):
def ssh_key_gen(self): def ssh_key_gen(self):
info = self.user_info() info = self.user_info()
if not os.path.exists(info[5]): if not os.path.exists(info[5]) and not self.module.check_mode:
return (1, '', 'User %s home directory does not exist' % self.name) return (1, '', 'User %s home directory does not exist' % self.name)
ssh_key_file = self.get_ssh_key_path() ssh_key_file = self.get_ssh_key_path()
ssh_dir = os.path.dirname(ssh_key_file) ssh_dir = os.path.dirname(ssh_key_file)
if not os.path.exists(ssh_dir): if not os.path.exists(ssh_dir):
if self.module.check_mode:
return (0, '', '')
try: try:
os.mkdir(ssh_dir, 0700) os.mkdir(ssh_dir, 0700)
os.chown(ssh_dir, info[2], info[3]) os.chown(ssh_dir, info[2], info[3])
@ -597,6 +599,8 @@ class User(object):
return (1, '', 'Failed to create %s: %s' % (ssh_dir, str(e))) return (1, '', 'Failed to create %s: %s' % (ssh_dir, str(e)))
if os.path.exists(ssh_key_file): if os.path.exists(ssh_key_file):
return (None, 'Key already exists', '') return (None, 'Key already exists', '')
if self.module.check_mode:
return (0, '', '')
cmd = [self.module.get_bin_path('ssh-keygen', True)] cmd = [self.module.get_bin_path('ssh-keygen', True)]
cmd.append('-t') cmd.append('-t')
cmd.append(self.ssh_type) cmd.append(self.ssh_type)
@ -2158,6 +2162,7 @@ def main():
# deal with ssh key # deal with ssh key
if user.sshkeygen: if user.sshkeygen:
# generate ssh key (note: this function is check mode aware)
(rc, out, err) = user.ssh_key_gen() (rc, out, err) = user.ssh_key_gen()
if rc is not None and rc != 0: if rc is not None and rc != 0:
module.fail_json(name=user.name, msg=err, rc=rc) module.fail_json(name=user.name, msg=err, rc=rc)

@ -25,7 +25,7 @@ $result = New-Object psobject @{
changed = $false changed = $false
}; };
$win32_os = Get-WmiObject Win32_OperatingSystem $win32_os = Get-CimInstance Win32_OperatingSystem
$osversion = [Environment]::OSVersion $osversion = [Environment]::OSVersion
$memory = @() $memory = @()
$memory += Get-WmiObject win32_Physicalmemory $memory += Get-WmiObject win32_Physicalmemory
@ -60,12 +60,15 @@ Set-Attr $result.ansible_facts "ansible_hostname" $env:COMPUTERNAME;
Set-Attr $result.ansible_facts "ansible_fqdn" "$([System.Net.Dns]::GetHostByName((hostname)).HostName)" Set-Attr $result.ansible_facts "ansible_fqdn" "$([System.Net.Dns]::GetHostByName((hostname)).HostName)"
Set-Attr $result.ansible_facts "ansible_system" $osversion.Platform.ToString() Set-Attr $result.ansible_facts "ansible_system" $osversion.Platform.ToString()
Set-Attr $result.ansible_facts "ansible_os_family" "Windows" Set-Attr $result.ansible_facts "ansible_os_family" "Windows"
Set-Attr $result.ansible_facts "ansible_os_name" $win32_os.Name.Split('|')[0] Set-Attr $result.ansible_facts "ansible_os_name" ($win32_os.Name.Split('|')[0]).Trim()
Set-Attr $result.ansible_facts "ansible_distribution" $osversion.VersionString Set-Attr $result.ansible_facts "ansible_distribution" $osversion.VersionString
Set-Attr $result.ansible_facts "ansible_distribution_version" $osversion.Version.ToString() Set-Attr $result.ansible_facts "ansible_distribution_version" $osversion.Version.ToString()
Set-Attr $result.ansible_facts "ansible_totalmem" $capacity Set-Attr $result.ansible_facts "ansible_totalmem" $capacity
Set-Attr $result.ansible_facts "ansible_lastboot" $win32_os.lastbootuptime.ToString("u")
Set-Attr $result.ansible_facts "ansible_uptime_seconds" $([System.Convert]::ToInt64($(Get-Date).Subtract($win32_os.lastbootuptime).TotalSeconds))
$ips = @() $ips = @()
Foreach ($ip in $netcfg.IPAddress) { If ($ip) { $ips += $ip } } Foreach ($ip in $netcfg.IPAddress) { If ($ip) { $ips += $ip } }
Set-Attr $result.ansible_facts "ansible_ip_addresses" $ips Set-Attr $result.ansible_facts "ansible_ip_addresses" $ips

@ -27,48 +27,18 @@ $result = New-Object PSObject -Property @{
changed = $false changed = $false
} }
If ($params.name) { $name = Get-Attr $params "name" -failifempty $true
$name = $params.name -split ',' | % { $_.Trim() } $name = $name -split ',' | % { $_.Trim() }
}
Else {
Fail-Json $result "mising required argument: name"
}
If ($params.state) {
$state = $params.state.ToString().ToLower()
If (($state -ne 'present') -and ($state -ne 'absent')) {
Fail-Json $result "state is '$state'; must be 'present' or 'absent'"
}
}
Elseif (!$params.state) {
$state = "present"
}
If ($params.restart) {
$restart = $params.restart | ConvertTo-Bool
}
Else
{
$restart = $false
}
if ($params.include_sub_features) $state = Get-Attr $params "state" "present"
{ $state = $state.ToString().ToLower()
$includesubfeatures = $params.include_sub_features | ConvertTo-Bool If (($state -ne 'present') -and ($state -ne 'absent')) {
} Fail-Json $result "state is '$state'; must be 'present' or 'absent'"
Else
{
$includesubfeatures = $false
} }
if ($params.include_management_tools) $restart = Get-Attr $params "restart" $false | ConvertTo-Bool
{ $includesubfeatures = Get-Attr $params "include_sub_features" $false | ConvertTo-Bool
$includemanagementtools = $params.include_management_tools | ConvertTo-Bool $includemanagementtools = Get-Attr $params "include_management_tools" $false | ConvertTo-Bool
}
Else
{
$includemanagementtools = $false
}
If ($state -eq "present") { If ($state -eq "present") {
try { try {

@ -40,22 +40,39 @@ Else {
Fail-Json $result "missing required argument: dest" Fail-Json $result "missing required argument: dest"
} }
$skip_certificate_validation = Get-Attr $params "skip_certificate_validation" $false | ConvertTo-Bool
$username = Get-Attr $params "username"
$password = Get-Attr $params "password"
if($skip_certificate_validation){
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
}
$force = Get-Attr -obj $params -name "force" "yes" | ConvertTo-Bool $force = Get-Attr -obj $params -name "force" "yes" | ConvertTo-Bool
If ($force -or -not (Test-Path $dest)) { If ($force -or -not (Test-Path $dest)) {
$client = New-Object System.Net.WebClient $client = New-Object System.Net.WebClient
if($username -and $password){
$client.Credentials = New-Object System.Net.NetworkCredential($username, $password)
}
Try { Try {
$client.DownloadFile($url, $dest) $client.DownloadFile($url, $dest)
$result.changed = $true $result.changed = $true
} }
Catch { Catch {
Fail-Json $result "Error downloading $url to $dest" Fail-Json $result "Error downloading $url to $dest $($_.Exception.Message)"
} }
} }
Else { Else {
Try { Try {
$webRequest = [System.Net.HttpWebRequest]::Create($url) $webRequest = [System.Net.HttpWebRequest]::Create($url)
if($username -and $password){
$webRequest.Credentials = New-Object System.Net.NetworkCredential($username, $password)
}
$webRequest.IfModifiedSince = ([System.IO.FileInfo]$dest).LastWriteTime $webRequest.IfModifiedSince = ([System.IO.FileInfo]$dest).LastWriteTime
$webRequest.Method = "GET" $webRequest.Method = "GET"
[System.Net.HttpWebResponse]$webResponse = $webRequest.GetResponse() [System.Net.HttpWebResponse]$webResponse = $webRequest.GetResponse()
@ -68,11 +85,11 @@ Else {
} }
Catch [System.Net.WebException] { Catch [System.Net.WebException] {
If ($_.Exception.Response.StatusCode -ne [System.Net.HttpStatusCode]::NotModified) { If ($_.Exception.Response.StatusCode -ne [System.Net.HttpStatusCode]::NotModified) {
Fail-Json $result "Error downloading $url to $dest" Fail-Json $result "Error downloading $url to $dest $($_.Exception.Message)"
} }
} }
Catch { Catch {
Fail-Json $result "Error downloading $url to $dest" Fail-Json $result "Error downloading $url to $dest $($_.Exception.Message)"
} }
} }

@ -28,6 +28,7 @@ version_added: "1.7"
short_description: Fetches a file from a given URL short_description: Fetches a file from a given URL
description: description:
- Fetches a file from a URL and saves to locally - Fetches a file from a URL and saves to locally
author: "Paul Durivage (@angstwad)"
options: options:
url: url:
description: description:
@ -49,7 +50,21 @@ options:
required: false required: false
choices: [ "yes", "no" ] choices: [ "yes", "no" ]
default: yes default: yes
author: "Paul Durivage (@angstwad)" username:
description:
- Basic authentication username
required: false
default: null
password:
description:
- Basic authentication password
required: false
default: null
skip_certificate_validation:
description:
- Skip SSL certificate validation if true
required: false
default: false
''' '''
EXAMPLES = ''' EXAMPLES = '''

@ -24,35 +24,31 @@ $params = Parse-Args $args;
$result = New-Object PSObject; $result = New-Object PSObject;
Set-Attr $result "changed" $false; Set-Attr $result "changed" $false;
If (-not $params.name.GetType) { $name = Get-Attr $params "name" -failifempty $true
Fail-Json $result "missing required arguments: name"
}
If ($params.state) { $state = Get-Attr $params "state" "present"
$state = $params.state.ToString().ToLower() $state = $state.ToString().ToLower()
If (($state -ne "present") -and ($state -ne "absent")) { If (($state -ne "present") -and ($state -ne "absent")) {
Fail-Json $result "state is '$state'; must be 'present' or 'absent'" Fail-Json $result "state is '$state'; must be 'present' or 'absent'"
}
}
Elseif (-not $params.state) {
$state = "present"
} }
$description = Get-Attr $params "description" $null
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME" $adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
$group = $adsi.Children | Where-Object {$_.SchemaClassName -eq 'group' -and $_.Name -eq $params.name } $group = $adsi.Children | Where-Object {$_.SchemaClassName -eq 'group' -and $_.Name -eq $name }
try { try {
If ($state -eq "present") { If ($state -eq "present") {
If (-not $group) { If (-not $group) {
$group = $adsi.Create("Group", $params.name) $group = $adsi.Create("Group", $name)
$group.SetInfo() $group.SetInfo()
Set-Attr $result "changed" $true Set-Attr $result "changed" $true
} }
If ($params.description.GetType) { If ($null -ne $description) {
IF (-not $group.description -or $group.description -ne $params.description) { IF (-not $group.description -or $group.description -ne $description) {
$group.description = $params.description $group.description = $description
$group.SetInfo() $group.SetInfo()
Set-Attr $result "changed" $true Set-Attr $result "changed" $true
} }

@ -21,36 +21,28 @@
$params = Parse-Args $args; $params = Parse-Args $args;
$result = New-Object psobject; $path = Get-Attr $params "path" -failifempty $true
Set-Attr $result "changed" $false; $state = Get-Attr $params "state" "present"
$creates = Get-Attr $params "creates" $false
$extra_args = Get-Attr $params "extra_args" ""
If (-not $params.path.GetType) $result = New-Object psobject @{
{ changed = $false
Fail-Json $result "missing required arguments: path" };
}
$extra_args = ""
If ($params.extra_args.GetType)
{
$extra_args = $params.extra_args;
}
If ($params.creates.GetType -and $params.state.GetType -and $params.state -ne "absent") If (($creates -ne $false) -and ($state -ne "absent") -and (Test-Path $creates))
{ {
If (Test-File $creates) Exit-Json $result;
{
Exit-Json $result;
}
} }
$logfile = [IO.Path]::GetTempFileName(); $logfile = [IO.Path]::GetTempFileName();
if ($params.state.GetType -and $params.state -eq "absent") if ($state -eq "absent")
{ {
msiexec.exe /x $params.path /qb /l $logfile $extra_args; msiexec.exe /x $path /qn /l $logfile $extra_args
} }
Else Else
{ {
msiexec.exe /i $params.path /qb /l $logfile $extra_args; msiexec.exe /i $path /qn /l $logfile $extra_args
} }
Set-Attr $result "changed" $true; Set-Attr $result "changed" $true;

@ -24,26 +24,25 @@ $params = Parse-Args $args;
$result = New-Object PSObject; $result = New-Object PSObject;
Set-Attr $result "changed" $false; Set-Attr $result "changed" $false;
If (-not $params.name.GetType) $name = Get-Attr $params "name" -failifempty $true
{ $state = Get-Attr $params "state" $false
Fail-Json $result "missing required arguments: name" $startMode = Get-Attr $params "start_mode" $false
}
If ($params.state) { If ($state) {
$state = $params.state.ToString().ToLower() $state = $state.ToString().ToLower()
If (($state -ne 'started') -and ($state -ne 'stopped') -and ($state -ne 'restarted')) { If (($state -ne 'started') -and ($state -ne 'stopped') -and ($state -ne 'restarted')) {
Fail-Json $result "state is '$state'; must be 'started', 'stopped', or 'restarted'" Fail-Json $result "state is '$state'; must be 'started', 'stopped', or 'restarted'"
} }
} }
If ($params.start_mode) { If ($startMode) {
$startMode = $params.start_mode.ToString().ToLower() $startMode = $startMode.ToString().ToLower()
If (($startMode -ne 'auto') -and ($startMode -ne 'manual') -and ($startMode -ne 'disabled')) { If (($startMode -ne 'auto') -and ($startMode -ne 'manual') -and ($startMode -ne 'disabled')) {
Fail-Json $result "start mode is '$startMode'; must be 'auto', 'manual', or 'disabled'" Fail-Json $result "start mode is '$startMode'; must be 'auto', 'manual', or 'disabled'"
} }
} }
$svcName = $params.name $svcName = $name
$svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue $svc = Get-Service -Name $svcName -ErrorAction SilentlyContinue
If (-not $svc) { If (-not $svc) {
Fail-Json $result "Service '$svcName' not installed" Fail-Json $result "Service '$svcName' not installed"

@ -42,14 +42,14 @@ If (Test-Path $path)
Set-Attr $result.stat "exists" $TRUE; Set-Attr $result.stat "exists" $TRUE;
$info = Get-Item $path; $info = Get-Item $path;
$epoch_date = Get-Date -Date "01/01/1970" $epoch_date = Get-Date -Date "01/01/1970"
If ($info.Directory) # Only files have the .Directory attribute. If ($info.PSIsContainer)
{ {
Set-Attr $result.stat "isdir" $FALSE; Set-Attr $result.stat "isdir" $TRUE;
Set-Attr $result.stat "size" $info.Length;
} }
Else Else
{ {
Set-Attr $result.stat "isdir" $TRUE; Set-Attr $result.stat "isdir" $FALSE;
Set-Attr $result.stat "size" $info.Length;
} }
Set-Attr $result.stat "extension" $info.Extension; Set-Attr $result.stat "extension" $info.Extension;
Set-Attr $result.stat "attributes" $info.Attributes.ToString(); Set-Attr $result.stat "attributes" $info.Attributes.ToString();

@ -16,7 +16,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# WANT_JSON
# POWERSHELL_COMMON # POWERSHELL_COMMON
######## ########
@ -55,33 +54,21 @@ $result = New-Object psobject @{
changed = $false changed = $false
}; };
If (-not $params.name.GetType) { $username = Get-Attr $params "name" -failifempty $true
Fail-Json $result "missing required arguments: name"
}
$username = Get-Attr $params "name"
$fullname = Get-Attr $params "fullname" $fullname = Get-Attr $params "fullname"
$description = Get-Attr $params "description" $description = Get-Attr $params "description"
$password = Get-Attr $params "password" $password = Get-Attr $params "password"
If ($params.state) { $state = Get-Attr $params "state" "present"
$state = $params.state.ToString().ToLower() $state = $state.ToString().ToLower()
If (($state -ne 'present') -and ($state -ne 'absent') -and ($state -ne 'query')) { If (($state -ne 'present') -and ($state -ne 'absent') -and ($state -ne 'query')) {
Fail-Json $result "state is '$state'; must be 'present', 'absent' or 'query'" Fail-Json $result "state is '$state'; must be 'present', 'absent' or 'query'"
}
}
ElseIf (!$params.state) {
$state = "present"
} }
If ($params.update_password) { $update_password = Get-Attr $params "update_password" "always"
$update_password = $params.update_password.ToString().ToLower() $update_password = $update_password.ToString().ToLower()
If (($update_password -ne 'always') -and ($update_password -ne 'on_create')) { If (($update_password -ne 'always') -and ($update_password -ne 'on_create')) {
Fail-Json $result "update_password is '$update_password'; must be 'always' or 'on_create'" Fail-Json $result "update_password is '$update_password'; must be 'always' or 'on_create'"
}
}
ElseIf (!$params.update_password) {
$update_password = "always"
} }
$password_expired = Get-Attr $params "password_expired" $null $password_expired = Get-Attr $params "password_expired" $null
@ -126,14 +113,10 @@ If ($groups -ne $null) {
} }
} }
If ($params.groups_action) { $groups_action = Get-Attr $params "groups_action" "replace"
$groups_action = $params.groups_action.ToString().ToLower() $groups_action = $groups_action.ToString().ToLower()
If (($groups_action -ne 'replace') -and ($groups_action -ne 'add') -and ($groups_action -ne 'remove')) { If (($groups_action -ne 'replace') -and ($groups_action -ne 'add') -and ($groups_action -ne 'remove')) {
Fail-Json $result "groups_action is '$groups_action'; must be 'replace', 'add' or 'remove'" Fail-Json $result "groups_action is '$groups_action'; must be 'replace', 'add' or 'remove'"
}
}
ElseIf (!$params.groups_action) {
$groups_action = "replace"
} }
$user_obj = Get-User $username $user_obj = Get-User $username
@ -141,7 +124,7 @@ $user_obj = Get-User $username
If ($state -eq 'present') { If ($state -eq 'present') {
# Add or update user # Add or update user
try { try {
If (!$user_obj.GetType) { If (-not $user_obj -or -not $user_obj.GetType) {
$user_obj = $adsi.Create("User", $username) $user_obj = $adsi.Create("User", $username)
If ($password -ne $null) { If ($password -ne $null) {
$user_obj.SetPassword($password) $user_obj.SetPassword($password)
@ -200,13 +183,13 @@ If ($state -eq 'present') {
If ($result.changed) { If ($result.changed) {
$user_obj.SetInfo() $user_obj.SetInfo()
} }
If ($groups.GetType) { If ($null -ne $groups) {
[string[]]$current_groups = $user_obj.Groups() | ForEach { $_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null) } [string[]]$current_groups = $user_obj.Groups() | ForEach { $_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null) }
If (($groups_action -eq "remove") -or ($groups_action -eq "replace")) { If (($groups_action -eq "remove") -or ($groups_action -eq "replace")) {
ForEach ($grp in $current_groups) { ForEach ($grp in $current_groups) {
If ((($groups_action -eq "remove") -and ($groups -contains $grp)) -or (($groups_action -eq "replace") -and ($groups -notcontains $grp))) { If ((($groups_action -eq "remove") -and ($groups -contains $grp)) -or (($groups_action -eq "replace") -and ($groups -notcontains $grp))) {
$group_obj = $adsi.Children | where { $_.SchemaClassName -eq 'Group' -and $_.Name -eq $grp } $group_obj = $adsi.Children | where { $_.SchemaClassName -eq 'Group' -and $_.Name -eq $grp }
If ($group_obj.GetType) { If ($group_obj -and $group_obj.GetType) {
$group_obj.Remove($user_obj.Path) $group_obj.Remove($user_obj.Path)
$result.changed = $true $result.changed = $true
} }
@ -239,7 +222,7 @@ If ($state -eq 'present') {
ElseIf ($state -eq 'absent') { ElseIf ($state -eq 'absent') {
# Remove user # Remove user
try { try {
If ($user_obj.GetType) { If ($user_obj -and $user_obj.GetType) {
$username = $user_obj.Name.Value $username = $user_obj.Name.Value
$adsi.delete("User", $user_obj.Name.Value) $adsi.delete("User", $user_obj.Name.Value)
$result.changed = $true $result.changed = $true
@ -252,7 +235,7 @@ ElseIf ($state -eq 'absent') {
} }
try { try {
If ($user_obj.GetType) { If ($user_obj -and $user_obj.GetType) {
$user_obj.RefreshCache() $user_obj.RefreshCache()
Set-Attr $result "name" $user_obj.Name[0] Set-Attr $result "name" $user_obj.Name[0]
Set-Attr $result "fullname" $user_obj.FullName[0] Set-Attr $result "fullname" $user_obj.FullName[0]

Loading…
Cancel
Save