Merge branch 'devel' into mysql_anon_user
Conflicts: database/mysql/mysql_user.py
This commit is contained in:
commit
b5d7becc29
47 changed files with 683 additions and 322 deletions
|
@ -247,6 +247,12 @@ options:
|
|||
required: false
|
||||
default: null
|
||||
aliases: ['network_interface']
|
||||
spot_launch_group:
|
||||
version_added: "2.1"
|
||||
description:
|
||||
- Launch group for spot request, see U(http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/how-spot-instances-work.html#spot-launch-group)
|
||||
required: false
|
||||
default: null
|
||||
|
||||
author:
|
||||
- "Tim Gerla (@tgerla)"
|
||||
|
@ -303,6 +309,22 @@ EXAMPLES = '''
|
|||
vpc_subnet_id: subnet-29e63245
|
||||
assign_public_ip: yes
|
||||
|
||||
# Single instance with ssd gp2 root volume
|
||||
- ec2:
|
||||
key_name: mykey
|
||||
group: webserver
|
||||
instance_type: c3.medium
|
||||
image: ami-123456
|
||||
wait: yes
|
||||
wait_timeout: 500
|
||||
volumes:
|
||||
- device_name: /dev/xvda
|
||||
volume_type: gp2
|
||||
volume_size: 8
|
||||
vpc_subnet_id: subnet-29e63245
|
||||
assign_public_ip: yes
|
||||
exact_count: 1
|
||||
|
||||
# Multiple groups example
|
||||
- ec2:
|
||||
key_name: mykey
|
||||
|
@ -358,6 +380,7 @@ EXAMPLES = '''
|
|||
wait: yes
|
||||
vpc_subnet_id: subnet-29e63245
|
||||
assign_public_ip: yes
|
||||
spot_launch_group: report_generators
|
||||
|
||||
# Examples using pre-existing network interfaces
|
||||
- ec2:
|
||||
|
@ -481,7 +504,6 @@ EXAMPLES = '''
|
|||
#
|
||||
|
||||
- ec2:
|
||||
state: running
|
||||
key_name: mykey
|
||||
instance_type: c1.medium
|
||||
image: ami-40603AD1
|
||||
|
@ -499,7 +521,6 @@ EXAMPLES = '''
|
|||
#
|
||||
|
||||
- ec2:
|
||||
state: running
|
||||
key_name: mykey
|
||||
instance_type: c1.medium
|
||||
image: ami-40603AD1
|
||||
|
@ -860,6 +881,7 @@ def create_instances(module, ec2, vpc, override_count=None):
|
|||
source_dest_check = module.boolean(module.params.get('source_dest_check'))
|
||||
termination_protection = module.boolean(module.params.get('termination_protection'))
|
||||
network_interfaces = module.params.get('network_interfaces')
|
||||
spot_launch_group = module.params.get('spot_launch_group')
|
||||
|
||||
# group_id and group_name are exclusive of each other
|
||||
if group_id and group_name:
|
||||
|
@ -883,6 +905,9 @@ def create_instances(module, ec2, vpc, override_count=None):
|
|||
grp_details = ec2.get_all_security_groups()
|
||||
if isinstance(group_name, basestring):
|
||||
group_name = [group_name]
|
||||
unmatched = set(group_name).difference(str(grp.name) for grp in grp_details)
|
||||
if len(unmatched) > 0:
|
||||
module.fail_json(msg="The following group names are not valid: %s" % ', '.join(unmatched))
|
||||
group_id = [ str(grp.id) for grp in grp_details if str(grp.name) in group_name ]
|
||||
# Now we try to lookup the group id testing if group exists.
|
||||
elif group_id:
|
||||
|
@ -1042,6 +1067,9 @@ def create_instances(module, ec2, vpc, override_count=None):
|
|||
module.fail_json(
|
||||
msg="placement_group parameter requires Boto version 2.3.0 or higher.")
|
||||
|
||||
if spot_launch_group and isinstance(spot_launch_group, basestring):
|
||||
params['launch_group'] = spot_launch_group
|
||||
|
||||
params.update(dict(
|
||||
count = count_remaining,
|
||||
type = spot_type,
|
||||
|
@ -1310,6 +1338,7 @@ def main():
|
|||
instance_type = dict(aliases=['type']),
|
||||
spot_price = dict(),
|
||||
spot_type = dict(default='one-time', choices=["one-time", "persistent"]),
|
||||
spot_launch_group = dict(),
|
||||
image = dict(),
|
||||
kernel = dict(),
|
||||
count = dict(type='int', default='1'),
|
||||
|
|
|
@ -152,9 +152,9 @@ EXAMPLES = '''
|
|||
|
||||
# Rolling ASG Updates
|
||||
|
||||
Below is an example of how to assign a new launch config to an ASG and terminate old instances.
|
||||
Below is an example of how to assign a new launch config to an ASG and terminate old instances.
|
||||
|
||||
All instances in "myasg" that do not have the launch configuration named "my_new_lc" will be terminated in
|
||||
All instances in "myasg" that do not have the launch configuration named "my_new_lc" will be terminated in
|
||||
a rolling fashion with instances using the current launch configuration, "my_new_lc".
|
||||
|
||||
This could also be considered a rolling deploy of a pre-baked AMI.
|
||||
|
@ -281,7 +281,6 @@ def get_properties(autoscaling_group):
|
|||
if getattr(autoscaling_group, "tags", None):
|
||||
properties['tags'] = dict((t.key, t.value) for t in autoscaling_group.tags)
|
||||
|
||||
|
||||
return properties
|
||||
|
||||
def elb_dreg(asg_connection, module, group_name, instance_id):
|
||||
|
@ -298,7 +297,6 @@ def elb_dreg(asg_connection, module, group_name, instance_id):
|
|||
else:
|
||||
return
|
||||
|
||||
exists = True
|
||||
for lb in as_group.load_balancers:
|
||||
elb_connection.deregister_instances(lb, instance_id)
|
||||
log.debug("De-registering {0} from ELB {1}".format(instance_id, lb))
|
||||
|
@ -315,10 +313,8 @@ def elb_dreg(asg_connection, module, group_name, instance_id):
|
|||
time.sleep(10)
|
||||
|
||||
if wait_timeout <= time.time():
|
||||
# waiting took too long
|
||||
# waiting took too long
|
||||
module.fail_json(msg = "Waited too long for instance to deregister. {0}".format(time.asctime()))
|
||||
|
||||
|
||||
|
||||
|
||||
def elb_healthy(asg_connection, elb_connection, module, group_name):
|
||||
|
@ -337,7 +333,7 @@ def elb_healthy(asg_connection, elb_connection, module, group_name):
|
|||
# but has not yet show up in the ELB
|
||||
try:
|
||||
lb_instances = elb_connection.describe_instance_health(lb, instances=instances)
|
||||
except boto.exception.InvalidInstance, e:
|
||||
except boto.exception.InvalidInstance:
|
||||
pass
|
||||
for i in lb_instances:
|
||||
if i.state == "InService":
|
||||
|
@ -346,7 +342,6 @@ def elb_healthy(asg_connection, elb_connection, module, group_name):
|
|||
return len(healthy_instances)
|
||||
|
||||
|
||||
|
||||
def wait_for_elb(asg_connection, module, group_name):
|
||||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
||||
wait_timeout = module.params.get('wait_timeout')
|
||||
|
@ -370,7 +365,7 @@ def wait_for_elb(asg_connection, module, group_name):
|
|||
log.debug("ELB thinks {0} instances are healthy.".format(healthy_instances))
|
||||
time.sleep(10)
|
||||
if wait_timeout <= time.time():
|
||||
# waiting took too long
|
||||
# waiting took too long
|
||||
module.fail_json(msg = "Waited too long for ELB instances to be healthy. %s" % time.asctime())
|
||||
log.debug("Waiting complete. ELB thinks {0} instances are healthy.".format(healthy_instances))
|
||||
|
||||
|
@ -396,7 +391,7 @@ def create_autoscaling_group(connection, module):
|
|||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
||||
try:
|
||||
ec2_connection = connect_to_aws(boto.ec2, region, **aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
module.fail_json(msg=str(e))
|
||||
elif vpc_zone_identifier:
|
||||
vpc_zone_identifier = ','.join(vpc_zone_identifier)
|
||||
|
@ -433,7 +428,7 @@ def create_autoscaling_group(connection, module):
|
|||
|
||||
try:
|
||||
connection.create_auto_scaling_group(ag)
|
||||
if wait_for_instances == True:
|
||||
if wait_for_instances:
|
||||
wait_for_new_inst(module, connection, group_name, wait_timeout, desired_capacity, 'viable_instances')
|
||||
wait_for_elb(connection, module, group_name)
|
||||
as_group = connection.get_all_groups(names=[group_name])[0]
|
||||
|
@ -475,7 +470,7 @@ def create_autoscaling_group(connection, module):
|
|||
dead_tags = []
|
||||
for tag in as_group.tags:
|
||||
have_tags[tag.key] = [tag.value, tag.propagate_at_launch]
|
||||
if not tag.key in want_tags:
|
||||
if tag.key not in want_tags:
|
||||
changed = True
|
||||
dead_tags.append(tag)
|
||||
|
||||
|
@ -492,14 +487,13 @@ def create_autoscaling_group(connection, module):
|
|||
changed = True
|
||||
as_group.load_balancers = module.params.get('load_balancers')
|
||||
|
||||
|
||||
if changed:
|
||||
try:
|
||||
as_group.update()
|
||||
except BotoServerError, e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
if wait_for_instances == True:
|
||||
if wait_for_instances:
|
||||
wait_for_new_inst(module, connection, group_name, wait_timeout, desired_capacity, 'viable_instances')
|
||||
wait_for_elb(connection, module, group_name)
|
||||
try:
|
||||
|
@ -525,7 +519,7 @@ def delete_autoscaling_group(connection, module):
|
|||
if tmp_groups:
|
||||
tmp_group = tmp_groups[0]
|
||||
if not tmp_group.instances:
|
||||
instances = False
|
||||
instances = False
|
||||
time.sleep(10)
|
||||
|
||||
group.delete()
|
||||
|
@ -580,15 +574,15 @@ def replace(connection, module):
|
|||
changed = True
|
||||
return(changed, props)
|
||||
|
||||
# we don't want to spin up extra instances if not necessary
|
||||
# we don't want to spin up extra instances if not necessary
|
||||
if num_new_inst_needed < batch_size:
|
||||
log.debug("Overriding batch size to {0}".format(num_new_inst_needed))
|
||||
batch_size = num_new_inst_needed
|
||||
log.debug("Overriding batch size to {0}".format(num_new_inst_needed))
|
||||
batch_size = num_new_inst_needed
|
||||
|
||||
if not old_instances:
|
||||
changed = False
|
||||
return(changed, props)
|
||||
|
||||
|
||||
#check if min_size/max_size/desired capacity have been specified and if not use ASG values
|
||||
if min_size is None:
|
||||
min_size = as_group.min_size
|
||||
|
@ -637,7 +631,7 @@ def get_instances_by_lc(props, lc_check, initial_instances):
|
|||
new_instances.append(i)
|
||||
else:
|
||||
old_instances.append(i)
|
||||
|
||||
|
||||
else:
|
||||
log.debug("Comparing initial instances with current: {0}".format(initial_instances))
|
||||
for i in props['instances']:
|
||||
|
@ -659,10 +653,10 @@ def list_purgeable_instances(props, lc_check, replace_instances, initial_instanc
|
|||
# and they have a non-current launch config
|
||||
if lc_check:
|
||||
for i in instances:
|
||||
if props['instance_facts'][i]['launch_config_name'] != props['launch_config_name']:
|
||||
if props['instance_facts'][i]['launch_config_name'] != props['launch_config_name']:
|
||||
instances_to_terminate.append(i)
|
||||
else:
|
||||
for i in instances:
|
||||
for i in instances:
|
||||
if i in initial_instances:
|
||||
instances_to_terminate.append(i)
|
||||
return instances_to_terminate
|
||||
|
@ -676,7 +670,7 @@ def terminate_batch(connection, module, replace_instances, initial_instances, le
|
|||
lc_check = module.params.get('lc_check')
|
||||
decrement_capacity = False
|
||||
break_loop = False
|
||||
|
||||
|
||||
as_group = connection.get_all_groups(names=[group_name])[0]
|
||||
props = get_properties(as_group)
|
||||
desired_size = as_group.min_size
|
||||
|
@ -720,7 +714,7 @@ def terminate_batch(connection, module, replace_instances, initial_instances, le
|
|||
elb_dreg(connection, module, group_name, instance_id)
|
||||
log.debug("terminating instance: {0}".format(instance_id))
|
||||
connection.terminate_instance(instance_id, decrement_capacity=decrement_capacity)
|
||||
|
||||
|
||||
# we wait to make sure the machines we marked as Unhealthy are
|
||||
# no longer in the list
|
||||
|
||||
|
@ -756,7 +750,7 @@ def wait_for_term_inst(connection, module, term_instances):
|
|||
# waiting took too long
|
||||
module.fail_json(msg = "Waited too long for old instances to terminate. %s" % time.asctime())
|
||||
|
||||
|
||||
|
||||
def wait_for_new_inst(module, connection, group_name, wait_timeout, desired_size, prop):
|
||||
|
||||
# make sure we have the latest stats after that last loop.
|
||||
|
@ -802,9 +796,9 @@ def main():
|
|||
termination_policies=dict(type='list', default='Default')
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive = [['replace_all_instances', 'replace_instances']]
|
||||
)
|
||||
|
||||
|
@ -826,13 +820,13 @@ def main():
|
|||
if state == 'present':
|
||||
create_changed, asg_properties=create_autoscaling_group(connection, module)
|
||||
elif state == 'absent':
|
||||
changed = delete_autoscaling_group(connection, module)
|
||||
module.exit_json( changed = changed )
|
||||
changed = delete_autoscaling_group(connection, module)
|
||||
module.exit_json( changed = changed )
|
||||
if replace_all_instances or replace_instances:
|
||||
replace_changed, asg_properties=replace(connection, module)
|
||||
if create_changed or replace_changed:
|
||||
changed = True
|
||||
module.exit_json( changed = changed, **asg_properties )
|
||||
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -50,10 +50,10 @@ options:
|
|||
choices: [ "yes", "no" ]
|
||||
wait:
|
||||
description:
|
||||
- Wait for instance registration or deregistration to complete successfully before returning.
|
||||
- Wait for instance registration or deregistration to complete successfully before returning.
|
||||
required: false
|
||||
default: yes
|
||||
choices: [ "yes", "no" ]
|
||||
choices: [ "yes", "no" ]
|
||||
validate_certs:
|
||||
description:
|
||||
- When set to "no", SSL certificates will not be validated for boto versions >= 2.6.0.
|
||||
|
@ -87,7 +87,7 @@ roles:
|
|||
- myrole
|
||||
post_tasks:
|
||||
- name: Instance Register
|
||||
local_action:
|
||||
local_action:
|
||||
module: ec2_elb
|
||||
instance_id: "{{ ansible_ec2_instance_id }}"
|
||||
ec2_elbs: "{{ item }}"
|
||||
|
@ -256,12 +256,23 @@ class ElbManager:
|
|||
ec2_elbs = self._get_auto_scaling_group_lbs()
|
||||
|
||||
try:
|
||||
elb = connect_to_aws(boto.ec2.elb, self.region,
|
||||
**self.aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
elb = connect_to_aws(boto.ec2.elb, self.region, **self.aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
self.module.fail_json(msg=str(e))
|
||||
|
||||
elbs = elb.get_all_load_balancers()
|
||||
elbs = []
|
||||
marker = None
|
||||
while True:
|
||||
try:
|
||||
newelbs = elb.get_all_load_balancers(marker=marker)
|
||||
marker = newelbs.next_marker
|
||||
elbs.extend(newelbs)
|
||||
if not marker:
|
||||
break
|
||||
except TypeError:
|
||||
# Older version of boto do not allow for params
|
||||
elbs = elb.get_all_load_balancers()
|
||||
break
|
||||
|
||||
if ec2_elbs:
|
||||
lbs = sorted(lb for lb in elbs if lb.name in ec2_elbs)
|
||||
|
@ -279,7 +290,7 @@ class ElbManager:
|
|||
|
||||
try:
|
||||
asg = connect_to_aws(boto.ec2.autoscale, self.region, **self.aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
self.module.fail_json(msg=str(e))
|
||||
|
||||
asg_instances = asg.get_all_autoscaling_instances([self.instance_id])
|
||||
|
@ -302,9 +313,8 @@ class ElbManager:
|
|||
def _get_instance(self):
|
||||
"""Returns a boto.ec2.InstanceObject for self.instance_id"""
|
||||
try:
|
||||
ec2 = connect_to_aws(boto.ec2, self.region,
|
||||
**self.aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
ec2 = connect_to_aws(boto.ec2, self.region, **self.aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
self.module.fail_json(msg=str(e))
|
||||
return ec2.get_only_instances(instance_ids=[self.instance_id])[0]
|
||||
|
||||
|
@ -330,7 +340,7 @@ def main():
|
|||
|
||||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
||||
|
||||
if not region:
|
||||
if not region:
|
||||
module.fail_json(msg="Region must be specified as a parameter, in EC2_REGION or AWS_REGION environment variables or in boto configuration file")
|
||||
|
||||
ec2_elbs = module.params['ec2_elbs']
|
||||
|
@ -342,8 +352,7 @@ def main():
|
|||
module.fail_json(msg="ELBs are required for registration")
|
||||
|
||||
instance_id = module.params['instance_id']
|
||||
elb_man = ElbManager(module, instance_id, ec2_elbs,
|
||||
region=region, **aws_connect_params)
|
||||
elb_man = ElbManager(module, instance_id, ec2_elbs, region=region, **aws_connect_params)
|
||||
|
||||
if ec2_elbs is not None:
|
||||
for elb in ec2_elbs:
|
||||
|
@ -365,4 +374,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -107,6 +107,7 @@ options:
|
|||
description:
|
||||
- Wait a specified timeout allowing connections to drain before terminating an instance
|
||||
required: false
|
||||
default: "None"
|
||||
aliases: []
|
||||
version_added: "1.8"
|
||||
idle_timeout:
|
||||
|
@ -491,7 +492,7 @@ class ElbManager(object):
|
|||
try:
|
||||
return connect_to_aws(boto.ec2.elb, self.region,
|
||||
**self.aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
self.module.fail_json(msg=str(e))
|
||||
|
||||
def _delete_elb(self):
|
||||
|
@ -980,4 +981,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -311,7 +311,7 @@ def main():
|
|||
|
||||
try:
|
||||
connection = connect_to_aws(boto.ec2.autoscale, region, **aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
||||
state = module.params.get('state')
|
||||
|
|
|
@ -115,8 +115,6 @@ EXAMPLES = '''
|
|||
|
||||
'''
|
||||
|
||||
import sys
|
||||
|
||||
try:
|
||||
import boto.ec2.cloudwatch
|
||||
from boto.ec2.cloudwatch import CloudWatchConnection, MetricAlarm
|
||||
|
@ -270,11 +268,11 @@ def main():
|
|||
state = module.params.get('state')
|
||||
|
||||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
||||
|
||||
|
||||
if region:
|
||||
try:
|
||||
connection = connect_to_aws(boto.ec2.cloudwatch, region, **aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
module.fail_json(msg=str(e))
|
||||
else:
|
||||
module.fail_json(msg="region must be specified")
|
||||
|
@ -288,4 +286,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -178,7 +178,7 @@ def main():
|
|||
|
||||
try:
|
||||
connection = connect_to_aws(boto.ec2.autoscale, region, **aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
module.fail_json(msg = str(e))
|
||||
|
||||
if state == 'present':
|
||||
|
@ -187,4 +187,5 @@ def main():
|
|||
delete_scaling_policy(connection, module)
|
||||
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -74,7 +74,7 @@ options:
|
|||
- If the volume's most recent snapshot has started less than `last_snapshot_min_age' minutes ago, a new snapshot will not be created.
|
||||
required: false
|
||||
default: 0
|
||||
version_added: "1.9"
|
||||
version_added: "2.0"
|
||||
|
||||
author: "Will Thames (@willthames)"
|
||||
extends_documentation_fragment:
|
||||
|
|
|
@ -47,7 +47,7 @@ options:
|
|||
volume_type:
|
||||
description:
|
||||
- Type of EBS volume; standard (magnetic), gp2 (SSD), io1 (Provisioned IOPS). "Standard" is the old EBS default
|
||||
and continues to remain the Ansible default for backwards compatibility.
|
||||
and continues to remain the Ansible default for backwards compatibility.
|
||||
required: false
|
||||
default: standard
|
||||
version_added: "1.9"
|
||||
|
@ -69,7 +69,7 @@ options:
|
|||
default: null
|
||||
zone:
|
||||
description:
|
||||
- zone in which to create the volume, if unset uses the zone the instance is in (if set)
|
||||
- zone in which to create the volume, if unset uses the zone the instance is in (if set)
|
||||
required: false
|
||||
default: null
|
||||
aliases: ['aws_zone', 'ec2_zone']
|
||||
|
@ -87,7 +87,7 @@ options:
|
|||
choices: ["yes", "no"]
|
||||
version_added: "1.5"
|
||||
state:
|
||||
description:
|
||||
description:
|
||||
- whether to ensure the volume is present or absent, or to list existing volumes (The C(list) option was added in version 1.8).
|
||||
required: false
|
||||
default: present
|
||||
|
@ -101,15 +101,15 @@ extends_documentation_fragment:
|
|||
|
||||
EXAMPLES = '''
|
||||
# Simple attachment action
|
||||
- ec2_vol:
|
||||
instance: XXXXXX
|
||||
volume_size: 5
|
||||
- ec2_vol:
|
||||
instance: XXXXXX
|
||||
volume_size: 5
|
||||
device_name: sdd
|
||||
|
||||
# Example using custom iops params
|
||||
# Example using custom iops params
|
||||
- ec2_vol:
|
||||
instance: XXXXXX
|
||||
volume_size: 5
|
||||
instance: XXXXXX
|
||||
volume_size: 5
|
||||
iops: 100
|
||||
device_name: sdd
|
||||
|
||||
|
@ -118,15 +118,15 @@ EXAMPLES = '''
|
|||
instance: XXXXXX
|
||||
snapshot: "{{ snapshot }}"
|
||||
|
||||
# Playbook example combined with instance launch
|
||||
# Playbook example combined with instance launch
|
||||
- ec2:
|
||||
keypair: "{{ keypair }}"
|
||||
image: "{{ image }}"
|
||||
wait: yes
|
||||
wait: yes
|
||||
count: 3
|
||||
register: ec2
|
||||
- ec2_vol:
|
||||
instance: "{{ item.id }} "
|
||||
instance: "{{ item.id }} "
|
||||
volume_size: 5
|
||||
with_items: ec2.instances
|
||||
register: ec2_vol
|
||||
|
@ -223,7 +223,7 @@ def get_volume(module, ec2):
|
|||
return vols[0]
|
||||
|
||||
def get_volumes(module, ec2):
|
||||
|
||||
|
||||
instance = module.params.get('instance')
|
||||
|
||||
try:
|
||||
|
@ -254,12 +254,10 @@ def boto_supports_volume_encryption():
|
|||
"""
|
||||
return hasattr(boto, 'Version') and LooseVersion(boto.Version) >= LooseVersion('2.29.0')
|
||||
|
||||
|
||||
|
||||
def create_volume(module, ec2, zone):
|
||||
changed = False
|
||||
name = module.params.get('name')
|
||||
id = module.params.get('id')
|
||||
instance = module.params.get('instance')
|
||||
iops = module.params.get('iops')
|
||||
encrypted = module.params.get('encrypted')
|
||||
volume_size = module.params.get('volume_size')
|
||||
|
@ -292,16 +290,16 @@ def create_volume(module, ec2, zone):
|
|||
|
||||
|
||||
def attach_volume(module, ec2, volume, instance):
|
||||
|
||||
|
||||
device_name = module.params.get('device_name')
|
||||
changed = False
|
||||
|
||||
|
||||
# If device_name isn't set, make a choice based on best practices here:
|
||||
# http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html
|
||||
|
||||
|
||||
# In future this needs to be more dynamic but combining block device mapping best practices
|
||||
# (bounds for devices, as above) with instance.block_device_mapping data would be tricky. For me ;)
|
||||
|
||||
|
||||
# Use password data attribute to tell whether the instance is Windows or Linux
|
||||
if device_name is None:
|
||||
try:
|
||||
|
@ -311,7 +309,7 @@ def attach_volume(module, ec2, volume, instance):
|
|||
device_name = '/dev/xvdf'
|
||||
except boto.exception.BotoServerError, e:
|
||||
module.fail_json(msg = "%s: %s" % (e.error_code, e.error_message))
|
||||
|
||||
|
||||
if volume.attachment_state() is not None:
|
||||
adata = volume.attach_data
|
||||
if adata.instance_id != instance.id:
|
||||
|
@ -330,9 +328,9 @@ def attach_volume(module, ec2, volume, instance):
|
|||
return volume, changed
|
||||
|
||||
def detach_volume(module, ec2, volume):
|
||||
|
||||
|
||||
changed = False
|
||||
|
||||
|
||||
if volume.attachment_state() is not None:
|
||||
adata = volume.attach_data
|
||||
volume.detach()
|
||||
|
@ -340,15 +338,15 @@ def detach_volume(module, ec2, volume):
|
|||
time.sleep(3)
|
||||
volume.update()
|
||||
changed = True
|
||||
|
||||
|
||||
return volume, changed
|
||||
|
||||
|
||||
def get_volume_info(volume, state):
|
||||
|
||||
|
||||
# If we're just listing volumes then do nothing, else get the latest update for the volume
|
||||
if state != 'list':
|
||||
volume.update()
|
||||
|
||||
|
||||
volume_info = {}
|
||||
attachment = volume.attach_data
|
||||
|
||||
|
@ -369,7 +367,7 @@ def get_volume_info(volume, state):
|
|||
},
|
||||
'tags': volume.tags
|
||||
}
|
||||
|
||||
|
||||
return volume_info
|
||||
|
||||
def main():
|
||||
|
@ -397,34 +395,32 @@ def main():
|
|||
name = module.params.get('name')
|
||||
instance = module.params.get('instance')
|
||||
volume_size = module.params.get('volume_size')
|
||||
volume_type = module.params.get('volume_type')
|
||||
iops = module.params.get('iops')
|
||||
encrypted = module.params.get('encrypted')
|
||||
device_name = module.params.get('device_name')
|
||||
zone = module.params.get('zone')
|
||||
snapshot = module.params.get('snapshot')
|
||||
state = module.params.get('state')
|
||||
|
||||
|
||||
# Ensure we have the zone or can get the zone
|
||||
if instance is None and zone is None and state == 'present':
|
||||
module.fail_json(msg="You must specify either instance or zone")
|
||||
|
||||
|
||||
# Set volume detach flag
|
||||
if instance == 'None' or instance == '':
|
||||
instance = None
|
||||
detach_vol_flag = True
|
||||
else:
|
||||
detach_vol_flag = False
|
||||
|
||||
|
||||
# Set changed flag
|
||||
changed = False
|
||||
|
||||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
||||
|
||||
|
||||
if region:
|
||||
try:
|
||||
ec2 = connect_to_aws(boto.ec2, region, **aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
module.fail_json(msg=str(e))
|
||||
else:
|
||||
module.fail_json(msg="region must be specified")
|
||||
|
@ -471,11 +467,11 @@ def main():
|
|||
|
||||
if volume_size and (id or snapshot):
|
||||
module.fail_json(msg="Cannot specify volume_size together with id or snapshot")
|
||||
|
||||
|
||||
if state == 'present':
|
||||
volume, changed = create_volume(module, ec2, zone)
|
||||
if detach_vol_flag:
|
||||
volume, changed = detach_volume(module, ec2, volume)
|
||||
volume, changed = detach_volume(module, ec2, volume)
|
||||
elif inst is not None:
|
||||
volume, changed = attach_volume(module, ec2, volume, inst)
|
||||
|
||||
|
@ -489,4 +485,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -93,9 +93,6 @@ EXAMPLES = '''
|
|||
|
||||
'''
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
try:
|
||||
import boto
|
||||
import boto.ec2
|
||||
|
@ -136,15 +133,15 @@ def vpc_exists(module, vpc, name, cidr_block, multi):
|
|||
module.fail_json(msg='Currently there are %d VPCs that have the same name and '
|
||||
'CIDR block you specified. If you would like to create '
|
||||
'the VPC anyway please pass True to the multi_ok param.' % len(matching_vpcs))
|
||||
|
||||
|
||||
return matched_vpc
|
||||
|
||||
|
||||
def update_vpc_tags(vpc, module, vpc_obj, tags, name):
|
||||
|
||||
|
||||
if tags is None:
|
||||
tags = dict()
|
||||
|
||||
|
||||
tags.update({'Name': name})
|
||||
try:
|
||||
current_tags = dict((t.name, t.value) for t in vpc.get_all_tags(filters={'resource-id': vpc_obj.id}))
|
||||
|
@ -156,10 +153,10 @@ def update_vpc_tags(vpc, module, vpc_obj, tags, name):
|
|||
except Exception, e:
|
||||
e_msg=boto_exception(e)
|
||||
module.fail_json(msg=e_msg)
|
||||
|
||||
|
||||
|
||||
def update_dhcp_opts(connection, module, vpc_obj, dhcp_id):
|
||||
|
||||
|
||||
if vpc_obj.dhcp_options_id != dhcp_id:
|
||||
connection.associate_dhcp_options(dhcp_id, vpc_obj.id)
|
||||
return True
|
||||
|
@ -211,48 +208,47 @@ def main():
|
|||
tags=module.params.get('tags')
|
||||
state=module.params.get('state')
|
||||
multi=module.params.get('multi_ok')
|
||||
|
||||
|
||||
changed=False
|
||||
|
||||
region, ec2_url, aws_connect_params = get_aws_connection_info(module)
|
||||
|
||||
|
||||
if region:
|
||||
try:
|
||||
connection = connect_to_aws(boto.vpc, region, **aws_connect_params)
|
||||
except (boto.exception.NoAuthHandlerFound, StandardError), e:
|
||||
except (boto.exception.NoAuthHandlerFound, AnsibleAWSError), e:
|
||||
module.fail_json(msg=str(e))
|
||||
else:
|
||||
module.fail_json(msg="region must be specified")
|
||||
|
||||
|
||||
if dns_hostnames and not dns_support:
|
||||
module.fail_json('In order to enable DNS Hostnames you must also enable DNS support')
|
||||
|
||||
if state == 'present':
|
||||
|
||||
|
||||
# Check if VPC exists
|
||||
vpc_obj = vpc_exists(module, connection, name, cidr_block, multi)
|
||||
|
||||
|
||||
if vpc_obj is None:
|
||||
try:
|
||||
vpc_obj = connection.create_vpc(cidr_block, instance_tenancy=tenancy)
|
||||
changed = True
|
||||
except BotoServerError, e:
|
||||
module.fail_json(msg=e)
|
||||
|
||||
if dhcp_id is not None:
|
||||
|
||||
if dhcp_id is not None:
|
||||
try:
|
||||
if update_dhcp_opts(connection, module, vpc_obj, dhcp_id):
|
||||
changed = True
|
||||
except BotoServerError, e:
|
||||
module.fail_json(msg=e)
|
||||
|
||||
if tags is not None or name is not None:
|
||||
|
||||
if tags is not None or name is not None:
|
||||
try:
|
||||
if update_vpc_tags(connection, module, vpc_obj, tags, name):
|
||||
changed = True
|
||||
except BotoServerError, e:
|
||||
module.fail_json(msg=e)
|
||||
|
||||
|
||||
# Note: Boto currently doesn't currently provide an interface to ec2-describe-vpc-attribute
|
||||
# which is needed in order to detect the current status of DNS options. For now we just update
|
||||
|
@ -263,21 +259,21 @@ def main():
|
|||
except BotoServerError, e:
|
||||
e_msg=boto_exception(e)
|
||||
module.fail_json(msg=e_msg)
|
||||
|
||||
|
||||
# get the vpc obj again in case it has changed
|
||||
try:
|
||||
vpc_obj = connection.get_all_vpcs(vpc_obj.id)[0]
|
||||
except BotoServerError, e:
|
||||
e_msg=boto_exception(e)
|
||||
module.fail_json(msg=e_msg)
|
||||
|
||||
|
||||
module.exit_json(changed=changed, vpc=get_vpc_values(vpc_obj))
|
||||
|
||||
elif state == 'absent':
|
||||
|
||||
|
||||
# Check if VPC exists
|
||||
vpc_obj = vpc_exists(module, connection, name, cidr_block, multi)
|
||||
|
||||
|
||||
if vpc_obj is not None:
|
||||
try:
|
||||
connection.delete_vpc(vpc_obj.id)
|
||||
|
@ -287,11 +283,12 @@ def main():
|
|||
e_msg = boto_exception(e)
|
||||
module.fail_json(msg="%s. You may want to use the ec2_vpc_subnet, ec2_vpc_igw, "
|
||||
"and/or ec2_vpc_route_table modules to ensure the other components are absent." % e_msg)
|
||||
|
||||
|
||||
module.exit_json(changed=changed, vpc=get_vpc_values(vpc_obj))
|
||||
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -192,14 +192,24 @@ def create_user(module, iam, name, pwd, path, key_state, key_count):
|
|||
|
||||
|
||||
def delete_user(module, iam, name):
|
||||
del_meta = ''
|
||||
try:
|
||||
current_keys = [ck['access_key_id'] for ck in
|
||||
iam.get_all_access_keys(name).list_access_keys_result.access_key_metadata]
|
||||
for key in current_keys:
|
||||
iam.delete_access_key(key, name)
|
||||
del_meta = iam.delete_user(name).delete_user_response
|
||||
except boto.exception.BotoServerError, err:
|
||||
error_msg = boto_exception(err)
|
||||
try:
|
||||
login_profile = iam.get_login_profiles(name).get_login_profile_response
|
||||
except boto.exception.BotoServerError, err:
|
||||
error_msg = boto_exception(err)
|
||||
if ('Cannot find Login Profile') in error_msg:
|
||||
|
||||
del_meta = iam.delete_user(name).delete_user_response
|
||||
else:
|
||||
iam.delete_login_profile(name)
|
||||
del_meta = iam.delete_user(name).delete_user_response
|
||||
except Exception as ex:
|
||||
module.fail_json(changed=False, msg="delete failed %s" %ex)
|
||||
if ('must detach all policies first') in error_msg:
|
||||
for policy in iam.get_all_user_policies(name).list_user_policies_result.policy_names:
|
||||
iam.delete_user_policy(name, policy)
|
||||
|
@ -213,7 +223,7 @@ def delete_user(module, iam, name):
|
|||
"currently supported by boto. Please detach the polices "
|
||||
"through the console and try again." % name)
|
||||
else:
|
||||
module.fail_json(changed=changed, msg=str(err))
|
||||
module.fail_json(changed=changed, msg=str(error_msg))
|
||||
else:
|
||||
changed = True
|
||||
return del_meta, name, changed
|
||||
|
@ -650,15 +660,20 @@ def main():
|
|||
else:
|
||||
module.exit_json(
|
||||
changed=changed, groups=user_groups, user_name=name, keys=key_list)
|
||||
|
||||
elif state == 'update' and not user_exists:
|
||||
module.fail_json(
|
||||
msg="The user %s does not exit. No update made." % name)
|
||||
|
||||
elif state == 'absent':
|
||||
if name in orig_user_list:
|
||||
set_users_groups(module, iam, name, '')
|
||||
del_meta, name, changed = delete_user(module, iam, name)
|
||||
module.exit_json(
|
||||
deletion_meta=del_meta, deleted_user=name, changed=changed)
|
||||
if user_exists:
|
||||
try:
|
||||
set_users_groups(module, iam, name, '')
|
||||
del_meta, name, changed = delete_user(module, iam, name)
|
||||
module.exit_json(deleted_user=name, changed=changed)
|
||||
|
||||
except Exception as ex:
|
||||
module.fail_json(changed=changed, msg=str(ex))
|
||||
else:
|
||||
module.exit_json(
|
||||
changed=False, msg="User %s is already absent from your AWS IAM users" % name)
|
||||
|
@ -690,9 +705,11 @@ def main():
|
|||
if not new_path and not new_name:
|
||||
module.exit_json(
|
||||
changed=changed, group_name=name, group_path=cur_path)
|
||||
|
||||
elif state == 'update' and not group_exists:
|
||||
module.fail_json(
|
||||
changed=changed, msg="Update Failed. Group %s doesn't seem to exit!" % name)
|
||||
|
||||
elif state == 'absent':
|
||||
if name in orig_group_list:
|
||||
removed_group, changed = delete_group(iam=iam, name=name)
|
||||
|
|
|
@ -64,9 +64,9 @@ extends_documentation_fragment:
|
|||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create and policy with the name of 'Admin' to the group 'administrators'
|
||||
# Create a policy with the name of 'Admin' to the group 'administrators'
|
||||
tasks:
|
||||
- name: Create two new IAM users with API keys
|
||||
- name: Assign a policy called Admin to the administrators group
|
||||
iam_policy:
|
||||
iam_type: group
|
||||
iam_name: administrators
|
||||
|
@ -87,7 +87,7 @@ task:
|
|||
- Luigi
|
||||
register: new_groups
|
||||
|
||||
- name:
|
||||
- name: Apply READ-ONLY policy to new groups that have been recently created
|
||||
iam_policy:
|
||||
iam_type: group
|
||||
iam_name: "{{ item.created_group.group_name }}"
|
||||
|
@ -188,7 +188,7 @@ def role_action(module, iam, name, policy_name, skip, pdoc, state):
|
|||
# Role doesn't exist so it's safe to assume the policy doesn't either
|
||||
module.exit_json(changed=False)
|
||||
else:
|
||||
module.fail_json(e.message)
|
||||
module.fail_json(msg=e.message)
|
||||
|
||||
try:
|
||||
for pol in current_policies:
|
||||
|
|
|
@ -829,13 +829,17 @@ def promote_db_instance(module, conn):
|
|||
instance_name = module.params.get('instance_name')
|
||||
|
||||
result = conn.get_db_instance(instance_name)
|
||||
if not result:
|
||||
module.fail_json(msg="DB Instance %s does not exist" % instance_name)
|
||||
|
||||
if result.get_data().get('replication_source'):
|
||||
changed = False
|
||||
else:
|
||||
try:
|
||||
result = conn.promote_read_replica(instance_name, **params)
|
||||
changed = True
|
||||
except RDSException, e:
|
||||
module.fail_json(msg=e.message)
|
||||
else:
|
||||
changed = False
|
||||
|
||||
if module.params.get('wait'):
|
||||
resource = await_resource(conn, result, 'available', module)
|
||||
|
|
|
@ -112,7 +112,7 @@ except ImportError:
|
|||
|
||||
# returns a tuple: (whether or not a parameter was changed, the remaining parameters that weren't found in this parameter group)
|
||||
|
||||
class NotModifiableError(StandardError):
|
||||
class NotModifiableError(Exception):
|
||||
def __init__(self, error_message, *args):
|
||||
super(NotModifiableError, self).__init__(error_message, *args)
|
||||
self.error_message = error_message
|
||||
|
@ -175,7 +175,7 @@ def modify_group(group, params, immediate=False):
|
|||
new_params = dict(params)
|
||||
|
||||
for key in new_params.keys():
|
||||
if group.has_key(key):
|
||||
if key in group:
|
||||
param = group[key]
|
||||
new_value = new_params[key]
|
||||
|
||||
|
@ -281,7 +281,6 @@ def main():
|
|||
else:
|
||||
break
|
||||
|
||||
|
||||
except BotoServerError, e:
|
||||
module.fail_json(msg = e.error_message)
|
||||
|
||||
|
@ -297,4 +296,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -295,7 +295,7 @@ def main():
|
|||
overwrite = dict(required=False, type='bool'),
|
||||
retry_interval = dict(required=False, default=500),
|
||||
private_zone = dict(required=False, type='bool', default=False),
|
||||
identifier = dict(required=False),
|
||||
identifier = dict(required=False, default=None),
|
||||
weight = dict(required=False, type='int'),
|
||||
region = dict(required=False),
|
||||
health_check = dict(required=False),
|
||||
|
@ -391,7 +391,7 @@ def main():
|
|||
#Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block
|
||||
rset.name = decoded_name
|
||||
|
||||
if rset.type == type_in and decoded_name.lower() == record_in.lower() and rset.identifier == str(identifier_in):
|
||||
if rset.type == type_in and decoded_name.lower() == record_in.lower() and str(rset.identifier) == str(identifier_in):
|
||||
found_record = True
|
||||
record['zone'] = zone_in
|
||||
record['type'] = rset.type
|
||||
|
|
|
@ -37,6 +37,7 @@ options:
|
|||
api_token:
|
||||
description:
|
||||
- DigitalOcean api token.
|
||||
version_added: "1.9.5"
|
||||
id:
|
||||
description:
|
||||
- Numeric, the droplet id you want to operate on.
|
||||
|
@ -100,8 +101,9 @@ options:
|
|||
|
||||
notes:
|
||||
- Two environment variables can be used, DO_API_KEY and DO_API_TOKEN. They both refer to the v2 token.
|
||||
- As of Ansible 2.0, Version 2 of the DigitalOcean API is used.
|
||||
- As of Ansible 2.0, the above parameters were changed significantly. If you are running 1.9.x or earlier, please use C(ansible-doc digital_ocean) to view the correct parameters for your version. Dedicated web docs will be available in the near future for the stable branch.
|
||||
- As of Ansible 1.9.5 and 2.0, Version 2 of the DigitalOcean API is used, this removes C(client_id) and C(api_key) options in favor of C(api_token).
|
||||
- If you are running Ansible 1.9.4 or earlier you might not be able to use the included version of this module as the API version used has been retired.
|
||||
Upgrade Ansible or, if unable to, try downloading the latest version of this module from github and putting it into a 'library' directory.
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
- dopy
|
||||
|
|
|
@ -29,12 +29,10 @@ options:
|
|||
- Indicate desired state of the target.
|
||||
default: present
|
||||
choices: ['present', 'absent']
|
||||
client_id:
|
||||
description:
|
||||
- DigitalOcean manager id.
|
||||
api_key:
|
||||
api_token:
|
||||
description:
|
||||
- DigitalOcean api key.
|
||||
- DigitalOcean api token.
|
||||
version_added: "1.9.5"
|
||||
id:
|
||||
description:
|
||||
- Numeric, the droplet id you want to operate on.
|
||||
|
@ -46,8 +44,9 @@ options:
|
|||
- The IP address to point a domain at.
|
||||
|
||||
notes:
|
||||
- Two environment variables can be used, DO_CLIENT_ID and DO_API_KEY.
|
||||
- Version 1 of DigitalOcean API is used.
|
||||
- Two environment variables can be used, DO_API_KEY and DO_API_TOKEN. They both refer to the v2 token.
|
||||
- As of Ansible 1.9.5 and 2.0, Version 2 of the DigitalOcean API is used, this removes C(client_id) and C(api_key) options in favor of C(api_token).
|
||||
- If you are running Ansible 1.9.4 or earlier you might not be able to use the included version of this module as the API version used has been retired.
|
||||
|
||||
requirements:
|
||||
- "python >= 2.6"
|
||||
|
@ -68,9 +67,9 @@ EXAMPLES = '''
|
|||
- digital_ocean: >
|
||||
state=present
|
||||
name=test_droplet
|
||||
size_id=1
|
||||
region_id=2
|
||||
image_id=3
|
||||
size_id=1gb
|
||||
region_id=sgp1
|
||||
image_id=ubuntu-14-04-x64
|
||||
register: test_droplet
|
||||
|
||||
- digital_ocean_domain: >
|
||||
|
@ -135,8 +134,8 @@ class Domain(JsonfyMixIn):
|
|||
return cls(json)
|
||||
|
||||
@classmethod
|
||||
def setup(cls, client_id, api_key):
|
||||
cls.manager = DoManager(client_id, api_key)
|
||||
def setup(cls, api_token):
|
||||
cls.manager = DoManager(None, api_token, api_version=2)
|
||||
DomainRecord.manager = cls.manager
|
||||
|
||||
@classmethod
|
||||
|
@ -171,16 +170,14 @@ def core(module):
|
|||
return v
|
||||
|
||||
try:
|
||||
# params['client_id'] will be None even if client_id is not passed in
|
||||
client_id = module.params['client_id'] or os.environ['DO_CLIENT_ID']
|
||||
api_key = module.params['api_key'] or os.environ['DO_API_KEY']
|
||||
api_token = module.params['api_token'] or os.environ['DO_API_TOKEN'] or os.environ['DO_API_KEY']
|
||||
except KeyError, e:
|
||||
module.fail_json(msg='Unable to load %s' % e.message)
|
||||
|
||||
changed = True
|
||||
state = module.params['state']
|
||||
|
||||
Domain.setup(client_id, api_key)
|
||||
Domain.setup(api_token)
|
||||
if state in ('present'):
|
||||
domain = Domain.find(id=module.params["id"])
|
||||
|
||||
|
@ -223,8 +220,7 @@ def main():
|
|||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(choices=['present', 'absent'], default='present'),
|
||||
client_id = dict(aliases=['CLIENT_ID'], no_log=True),
|
||||
api_key = dict(aliases=['API_KEY'], no_log=True),
|
||||
api_token = dict(aliases=['API_TOKEN'], no_log=True),
|
||||
name = dict(type='str'),
|
||||
id = dict(aliases=['droplet_id'], type='int'),
|
||||
ip = dict(type='str'),
|
||||
|
|
|
@ -79,8 +79,11 @@ options:
|
|||
version_added: "1.5"
|
||||
volumes:
|
||||
description:
|
||||
- List of volumes to mount within the container using docker CLI-style
|
||||
- 'syntax: C(/host:/container[:mode]) where "mode" may be "rw" or "ro".'
|
||||
- List of volumes to mount within the container
|
||||
- 'Use docker CLI-style syntax: C(/host:/container[:mode])'
|
||||
- You can specify a read mode for the mount with either C(ro) or C(rw).
|
||||
Starting at version 2.1, SELinux hosts can additionally use C(z) or C(Z)
|
||||
mount options to use a shared or private label for the volume.
|
||||
default: null
|
||||
volumes_from:
|
||||
description:
|
||||
|
@ -626,14 +629,14 @@ class DockerManager(object):
|
|||
# host mount (e.g. /mnt:/tmp, bind mounts host's /tmp to /mnt in the container)
|
||||
elif 2 <= len(parts) <= 3:
|
||||
# default to read-write
|
||||
ro = False
|
||||
mode = 'rw'
|
||||
# with supplied bind mode
|
||||
if len(parts) == 3:
|
||||
if parts[2] not in ['ro', 'rw']:
|
||||
self.module.fail_json(msg='bind mode needs to either be "ro" or "rw"')
|
||||
if parts[2] not in ["rw", "rw,Z", "rw,z", "z,rw", "Z,rw", "Z", "z", "ro", "ro,Z", "ro,z", "z,ro", "Z,ro"]:
|
||||
self.module.fail_json(msg='invalid bind mode ' + parts[2])
|
||||
else:
|
||||
ro = parts[2] == 'ro'
|
||||
self.binds[parts[0]] = {'bind': parts[1], 'ro': ro }
|
||||
mode = parts[2]
|
||||
self.binds[parts[0]] = {'bind': parts[1], 'mode': mode }
|
||||
else:
|
||||
self.module.fail_json(msg='volumes support 1 to 3 arguments')
|
||||
|
||||
|
@ -1197,10 +1200,7 @@ class DockerManager(object):
|
|||
for host_path, config in self.binds.iteritems():
|
||||
if isinstance(config, dict):
|
||||
container_path = config['bind']
|
||||
if config['ro']:
|
||||
mode = 'ro'
|
||||
else:
|
||||
mode = 'rw'
|
||||
mode = config['mode']
|
||||
else:
|
||||
container_path = config
|
||||
mode = 'rw'
|
||||
|
|
|
@ -164,18 +164,17 @@ def _get_neutron_client(module, kwargs):
|
|||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
tenant_name = module.params['login_tenant_name']
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
|
||||
def _get_net_id(neutron, module):
|
||||
kwargs = {
|
||||
'tenant_id': _os_tenant_id,
|
||||
|
|
|
@ -136,17 +136,16 @@ def _get_neutron_client(module, kwargs):
|
|||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
login_tenant_name = module.params['login_tenant_name']
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
login_tenant_name = module.params['tenant_name']
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == login_tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
def _get_router_id(module, neutron):
|
||||
kwargs = {
|
||||
|
|
|
@ -138,18 +138,17 @@ def _get_neutron_client(module, kwargs):
|
|||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
login_tenant_name = module.params['login_tenant_name']
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
login_tenant_name = module.params['tenant_name']
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == login_tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
|
||||
def _get_router_id(module, neutron):
|
||||
kwargs = {
|
||||
'name': module.params['router_name'],
|
||||
|
|
|
@ -170,16 +170,16 @@ def _get_neutron_client(module, kwargs):
|
|||
def _set_tenant_id(module):
|
||||
global _os_tenant_id
|
||||
if not module.params['tenant_name']:
|
||||
tenant_name = module.params['login_tenant_name']
|
||||
_os_tenant_id = _os_keystone.tenant_id
|
||||
else:
|
||||
tenant_name = module.params['tenant_name']
|
||||
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
for tenant in _os_keystone.tenants.list():
|
||||
if tenant.name == tenant_name:
|
||||
_os_tenant_id = tenant.id
|
||||
break
|
||||
if not _os_tenant_id:
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
module.fail_json(msg = "The tenant id cannot be found, please check the parameters")
|
||||
|
||||
def _get_net_id(neutron, module):
|
||||
kwargs = {
|
||||
|
|
|
@ -154,7 +154,7 @@ def main():
|
|||
msg="server {0} not found".format(server_name_or_id))
|
||||
|
||||
if state == 'present':
|
||||
cloud.add_ips_to_server(
|
||||
server = cloud.add_ips_to_server(
|
||||
server=server, ips=floating_ip_address, reuse=reuse,
|
||||
fixed_address=fixed_address, wait=wait, timeout=timeout)
|
||||
fip_address = cloud.get_server_public_ip(server)
|
||||
|
|
|
@ -56,12 +56,12 @@ options:
|
|||
default: None
|
||||
min_disk:
|
||||
description:
|
||||
- The minimum disk space required to deploy this image
|
||||
- The minimum disk space (in GB) required to boot this image
|
||||
required: false
|
||||
default: None
|
||||
min_ram:
|
||||
description:
|
||||
- The minimum ram required to deploy this image
|
||||
- The minimum ram (in MB) required to boot this image
|
||||
required: false
|
||||
default: None
|
||||
is_public:
|
||||
|
@ -125,8 +125,8 @@ def main():
|
|||
disk_format = dict(default='qcow2', choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso']),
|
||||
container_format = dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova']),
|
||||
owner = dict(default=None),
|
||||
min_disk = dict(default=None),
|
||||
min_ram = dict(default=None),
|
||||
min_disk = dict(type='int', default=0),
|
||||
min_ram = dict(type='int', default=0),
|
||||
is_public = dict(default=False),
|
||||
filename = dict(default=None),
|
||||
ramdisk = dict(default=None),
|
||||
|
@ -156,6 +156,8 @@ def main():
|
|||
wait=module.params['wait'],
|
||||
timeout=module.params['timeout'],
|
||||
is_public=module.params['is_public'],
|
||||
min_disk=module.params['min_disk'],
|
||||
min_ram=module.params['min_ram']
|
||||
)
|
||||
changed = True
|
||||
if not module.params['wait']:
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
try:
|
||||
import shade
|
||||
from shade import meta
|
||||
HAS_SHADE = True
|
||||
except ImportError:
|
||||
HAS_SHADE = False
|
||||
|
@ -28,6 +27,7 @@ module: os_user_group
|
|||
short_description: Associate OpenStack Identity users and groups
|
||||
extends_documentation_fragment: openstack
|
||||
version_added: "2.0"
|
||||
author: "Monty Taylor (@emonty)"
|
||||
description:
|
||||
- Add and remove users from groups
|
||||
options:
|
||||
|
@ -51,57 +51,66 @@ requirements:
|
|||
|
||||
EXAMPLES = '''
|
||||
# Add the demo user to the demo group
|
||||
- os_user_group: user=demo group=demo
|
||||
- os_user_group:
|
||||
cloud: mycloud
|
||||
user: demo
|
||||
group: demo
|
||||
'''
|
||||
|
||||
|
||||
def main():
|
||||
def _system_state_change(state, in_group):
|
||||
if state == 'present' and not in_group:
|
||||
return True
|
||||
if state == 'absent' and in_group:
|
||||
return True
|
||||
return False
|
||||
|
||||
def main():
|
||||
argument_spec = openstack_full_argument_spec(
|
||||
argument_spec = dict(
|
||||
user=dict(required=True),
|
||||
group=dict(required=True),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
))
|
||||
)
|
||||
|
||||
module_kwargs = openstack_module_kwargs()
|
||||
module = AnsibleModule(argument_spec, **module_kwargs)
|
||||
module = AnsibleModule(argument_spec,
|
||||
supports_check_mode=True,
|
||||
**module_kwargs)
|
||||
|
||||
if not HAS_SHADE:
|
||||
module.fail_json(msg='shade is required for this module')
|
||||
|
||||
user = module.params.pop('user')
|
||||
group = module.params.pop('group')
|
||||
state = module.params.pop('state')
|
||||
user = module.params['user']
|
||||
group = module.params['group']
|
||||
state = module.params['state']
|
||||
|
||||
try:
|
||||
cloud = shade.openstack_cloud(**module.params)
|
||||
cloud = shade.operator_cloud(**module.params)
|
||||
|
||||
in_group = cloud.is_user_in_group(user, group)
|
||||
|
||||
if state == 'present':
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=_system_state_change(state, in_group))
|
||||
|
||||
if in_group:
|
||||
changed = False
|
||||
else:
|
||||
cloud.add_user_to_group(
|
||||
user_name_or_id=user, group_name_or_id=group)
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if not in_group:
|
||||
cloud.add_user_to_group(user, group)
|
||||
changed = True
|
||||
|
||||
elif state == 'absent':
|
||||
if in_group:
|
||||
cloud.remove_user_from_group(
|
||||
user_name_or_id=user, group_name_or_id=group)
|
||||
cloud.remove_user_from_group(user, group)
|
||||
changed=True
|
||||
else:
|
||||
changed=False
|
||||
|
||||
module.exit_json(changed=changed)
|
||||
|
||||
except shade.OpenStackCloudException as e:
|
||||
module.fail_json(msg=e.message, extra_data=e.extra_data)
|
||||
|
||||
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.openstack import *
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/python
|
||||
#!/usr/bin/env python2
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of Ansible
|
||||
|
@ -170,6 +170,7 @@ EXAMPLES = '''
|
|||
vcpu.hotadd: yes
|
||||
mem.hotadd: yes
|
||||
notes: This is a test VM
|
||||
folder: MyFolder
|
||||
vm_disk:
|
||||
disk1:
|
||||
size_gb: 10
|
||||
|
@ -241,6 +242,8 @@ EXAMPLES = '''
|
|||
template_src: centosTemplate
|
||||
cluster: MainCluster
|
||||
resource_pool: "/Resources"
|
||||
vm_extra_config:
|
||||
folder: MyFolder
|
||||
|
||||
# Task to gather facts from a vSphere cluster only if the system is a VMWare guest
|
||||
|
||||
|
@ -597,7 +600,7 @@ def vmdisk_id(vm, current_datastore_name):
|
|||
return id_list
|
||||
|
||||
|
||||
def deploy_template(vsphere_client, guest, resource_pool, template_src, esxi, module, cluster_name, snapshot_to_clone, power_on_after_clone):
|
||||
def deploy_template(vsphere_client, guest, resource_pool, template_src, esxi, module, cluster_name, snapshot_to_clone, power_on_after_clone, vm_extra_config):
|
||||
vmTemplate = vsphere_client.get_vm_by_name(template_src)
|
||||
vmTarget = None
|
||||
|
||||
|
@ -689,6 +692,10 @@ def deploy_template(vsphere_client, guest, resource_pool, template_src, esxi, mo
|
|||
cloneArgs["linked"] = True
|
||||
cloneArgs["snapshot"] = snapshot_to_clone
|
||||
|
||||
if vm_extra_config.get("folder") is not None:
|
||||
# if a folder is specified, clone the VM into it
|
||||
cloneArgs["folder"] = vm_extra_config.get("folder")
|
||||
|
||||
vmTemplate.clone(guest, **cloneArgs)
|
||||
changed = True
|
||||
else:
|
||||
|
@ -701,12 +708,77 @@ def deploy_template(vsphere_client, guest, resource_pool, template_src, esxi, mo
|
|||
msg="Could not clone selected machine: %s" % e
|
||||
)
|
||||
|
||||
# example from https://github.com/kalazzerx/pysphere/blob/master/examples/pysphere_create_disk_and_add_to_vm.py
|
||||
# was used.
|
||||
def update_disks(vsphere_client, vm, module, vm_disk, changes):
|
||||
request = VI.ReconfigVM_TaskRequestMsg()
|
||||
changed = False
|
||||
|
||||
for cnf_disk in vm_disk:
|
||||
disk_id = re.sub("disk", "", cnf_disk)
|
||||
found = False
|
||||
for dev_key in vm._devices:
|
||||
if vm._devices[dev_key]['type'] == 'VirtualDisk':
|
||||
hdd_id = vm._devices[dev_key]['label'].split()[2]
|
||||
if disk_id == hdd_id:
|
||||
found = True
|
||||
continue
|
||||
if not found:
|
||||
it = VI.ReconfigVM_TaskRequestMsg()
|
||||
_this = request.new__this(vm._mor)
|
||||
_this.set_attribute_type(vm._mor.get_attribute_type())
|
||||
request.set_element__this(_this)
|
||||
|
||||
spec = request.new_spec()
|
||||
|
||||
dc = spec.new_deviceChange()
|
||||
dc.Operation = "add"
|
||||
dc.FileOperation = "create"
|
||||
|
||||
hd = VI.ns0.VirtualDisk_Def("hd").pyclass()
|
||||
hd.Key = -100
|
||||
hd.UnitNumber = int(disk_id)
|
||||
hd.CapacityInKB = int(vm_disk[cnf_disk]['size_gb']) * 1024 * 1024
|
||||
hd.ControllerKey = 1000
|
||||
|
||||
# module.fail_json(msg="peos : %s" % vm_disk[cnf_disk])
|
||||
backing = VI.ns0.VirtualDiskFlatVer2BackingInfo_Def("backing").pyclass()
|
||||
backing.FileName = "[%s]" % vm_disk[cnf_disk]['datastore']
|
||||
backing.DiskMode = "persistent"
|
||||
backing.Split = False
|
||||
backing.WriteThrough = False
|
||||
backing.ThinProvisioned = False
|
||||
backing.EagerlyScrub = False
|
||||
hd.Backing = backing
|
||||
|
||||
dc.Device = hd
|
||||
|
||||
spec.DeviceChange = [dc]
|
||||
request.set_element_spec(spec)
|
||||
|
||||
ret = vsphere_client._proxy.ReconfigVM_Task(request)._returnval
|
||||
|
||||
# Wait for the task to finish
|
||||
task = VITask(ret, vsphere_client)
|
||||
status = task.wait_for_state([task.STATE_SUCCESS,
|
||||
task.STATE_ERROR])
|
||||
|
||||
if status == task.STATE_SUCCESS:
|
||||
changed = True
|
||||
changes[cnf_disk] = vm_disk[cnf_disk]
|
||||
elif status == task.STATE_ERROR:
|
||||
module.fail_json(
|
||||
msg="Error reconfiguring vm: %s, [%s]" % (
|
||||
task.get_error_message(),
|
||||
vm_disk[cnf_disk]))
|
||||
return changed, changes
|
||||
|
||||
|
||||
def reconfigure_vm(vsphere_client, vm, module, esxi, resource_pool, cluster_name, guest, vm_extra_config, vm_hardware, vm_disk, vm_nic, state, force):
|
||||
spec = None
|
||||
changed = False
|
||||
changes = {}
|
||||
request = VI.ReconfigVM_TaskRequestMsg()
|
||||
request = None
|
||||
shutdown = False
|
||||
poweron = vm.is_powered_on()
|
||||
|
||||
|
@ -714,6 +786,10 @@ def reconfigure_vm(vsphere_client, vm, module, esxi, resource_pool, cluster_name
|
|||
cpuHotAddEnabled = bool(vm.properties.config.cpuHotAddEnabled)
|
||||
cpuHotRemoveEnabled = bool(vm.properties.config.cpuHotRemoveEnabled)
|
||||
|
||||
changed, changes = update_disks(vsphere_client, vm,
|
||||
module, vm_disk, changes)
|
||||
request = VI.ReconfigVM_TaskRequestMsg()
|
||||
|
||||
# Change Memory
|
||||
if 'memory_mb' in vm_hardware:
|
||||
|
||||
|
@ -1556,7 +1632,8 @@ def main():
|
|||
module=module,
|
||||
cluster_name=cluster,
|
||||
snapshot_to_clone=snapshot_to_clone,
|
||||
power_on_after_clone=power_on_after_clone
|
||||
power_on_after_clone=power_on_after_clone,
|
||||
vm_extra_config=vm_extra_config
|
||||
)
|
||||
|
||||
if state in ['restarted', 'reconfigured']:
|
||||
|
|
|
@ -47,12 +47,12 @@ options:
|
|||
default: null
|
||||
creates:
|
||||
description:
|
||||
- a filename or glob pattern, when it already exists, this step will B(not) be run.
|
||||
- a filename or (since 2.0) glob pattern, when it already exists, this step will B(not) be run.
|
||||
required: no
|
||||
default: null
|
||||
removes:
|
||||
description:
|
||||
- a filename or glob pattern, when it does not exist, this step will B(not) be run.
|
||||
- a filename or (since 2.0) glob pattern, when it does not exist, this step will B(not) be run.
|
||||
version_added: "0.8"
|
||||
required: no
|
||||
default: null
|
||||
|
|
|
@ -30,7 +30,7 @@ options:
|
|||
name:
|
||||
description:
|
||||
- name of the database to add or remove
|
||||
- name=all May only be provided if I(state) is C(dump) or C(import).
|
||||
- name=all May only be provided if I(state) is C(dump) or C(import).
|
||||
- if name=all Works like --all-databases option for mysqldump (Added in 2.0)
|
||||
required: true
|
||||
default: null
|
||||
|
@ -85,12 +85,15 @@ notes:
|
|||
- Requires the MySQLdb Python package on the remote host. For Ubuntu, this
|
||||
is as easy as apt-get install python-mysqldb. (See M(apt).) For CentOS/Fedora, this
|
||||
is as easy as yum install MySQL-python. (See M(yum).)
|
||||
- Requires the mysql command line client. For Centos/Fedora, this is as easy as
|
||||
yum install mariadb (See M(yum).). For Debian/Ubuntu this is as easy as
|
||||
apt-get install mariadb-client. (See M(apt).)
|
||||
- Both I(login_password) and I(login_user) are required when you are
|
||||
passing credentials. If none are present, the module will attempt to read
|
||||
the credentials from C(~/.my.cnf), and finally fall back to using the MySQL
|
||||
default login of C(root) with no password.
|
||||
requirements: [ ConfigParser ]
|
||||
author: "Mark Theunissen (@marktheunissen)"
|
||||
author: "Ansible Core Team"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
|
@ -367,7 +370,7 @@ def main():
|
|||
except Exception, e:
|
||||
module.fail_json(msg="error deleting database: " + str(e))
|
||||
elif state == "dump":
|
||||
rc, stdout, stderr = db_dump(module, login_host, login_user,
|
||||
rc, stdout, stderr = db_dump(module, login_host, login_user,
|
||||
login_password, db, target, all_databases,
|
||||
port=login_port,
|
||||
socket=module.params['login_unix_socket'])
|
||||
|
@ -376,7 +379,7 @@ def main():
|
|||
else:
|
||||
module.exit_json(changed=True, db=db, msg=stdout)
|
||||
elif state == "import":
|
||||
rc, stdout, stderr = db_import(module, login_host, login_user,
|
||||
rc, stdout, stderr = db_import(module, login_host, login_user,
|
||||
login_password, db, target, all_databases,
|
||||
port=login_port,
|
||||
socket=module.params['login_unix_socket'])
|
||||
|
|
|
@ -42,6 +42,13 @@ options:
|
|||
- set the user's password. (Required when adding a user)
|
||||
required: false
|
||||
default: null
|
||||
encrypted:
|
||||
description:
|
||||
- Indicate that the 'password' field is a `mysql_native_password` hash
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: "no"
|
||||
version_added: "2.0"
|
||||
host:
|
||||
description:
|
||||
- the 'host' part of the MySQL username
|
||||
|
@ -133,15 +140,19 @@ notes:
|
|||
without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing
|
||||
the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from
|
||||
the file."
|
||||
- Currently, there is only support for the `mysql_native_password` encryted password hash module.
|
||||
|
||||
requirements: [ "MySQLdb" ]
|
||||
author: "Mark Theunissen (@marktheunissen)"
|
||||
author: "Jonathan Mainguy (@Jmainguy)"
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
# Create database user with name 'bob' and password '12345' with all database privileges
|
||||
- mysql_user: name=bob password=12345 priv=*.*:ALL state=present
|
||||
|
||||
# Create database user with name 'bob' and previously hashed mysql native password '*EE0D72C1085C46C5278932678FBE2C6A782821B4' with all database privileges
|
||||
- mysql_user: name=bob password='*EE0D72C1085C46C5278932678FBE2C6A782821B4' encrypted=yes priv=*.*:ALL state=present
|
||||
|
||||
# Creates database user 'bob' and password '12345' with all database privileges and 'WITH GRANT OPTION'
|
||||
- mysql_user: name=bob password=12345 priv=*.*:ALL,GRANT state=present
|
||||
|
||||
|
@ -157,7 +168,7 @@ EXAMPLES = """
|
|||
# Specify grants composed of more than one word
|
||||
- mysql_user: name=replication password=12345 priv=*.*:"REPLICATION CLIENT" state=present
|
||||
|
||||
# Revoke all privileges for user 'bob' and password '12345'
|
||||
# Revoke all privileges for user 'bob' and password '12345'
|
||||
- mysql_user: name=bob password=12345 priv=*.*:USAGE state=present
|
||||
|
||||
# Example privileges string format
|
||||
|
@ -176,6 +187,7 @@ password=n<_665{vS43y
|
|||
import getpass
|
||||
import tempfile
|
||||
import re
|
||||
import string
|
||||
try:
|
||||
import MySQLdb
|
||||
except ImportError:
|
||||
|
@ -224,6 +236,19 @@ def connect(module, login_user=None, login_password=None, config_file=''):
|
|||
db_connection = MySQLdb.connect(**config)
|
||||
return db_connection.cursor()
|
||||
|
||||
# User Authentication Management was change in MySQL 5.7
|
||||
# This is a generic check for if the server version is less than version 5.7
|
||||
def server_version_check(cursor):
|
||||
cursor.execute("SELECT VERSION()");
|
||||
result = cursor.fetchone()
|
||||
version_str = result[0]
|
||||
version = version_str.split('.')
|
||||
|
||||
if (int(version[0]) <= 5 and int(version[1]) < 7):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def user_exists(cursor, user, host, host_all):
|
||||
if host_all:
|
||||
cursor.execute("SELECT count(*) FROM user WHERE user = %s", user)
|
||||
|
@ -233,38 +258,95 @@ def user_exists(cursor, user, host, host_all):
|
|||
count = cursor.fetchone()
|
||||
return count[0] > 0
|
||||
|
||||
def user_add(cursor, user, host, host_all, password, new_priv):
|
||||
def user_add(cursor, user, host, host_all, password, encrypted, new_priv):
|
||||
# we cannot create users without a proper hostname
|
||||
if host_all:
|
||||
return False
|
||||
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
|
||||
if password and encrypted:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user,host,password))
|
||||
elif password and not encrypted:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
|
||||
|
||||
if new_priv is not None:
|
||||
for db_table, priv in new_priv.iteritems():
|
||||
privileges_grant(cursor, user,host,db_table,priv)
|
||||
return True
|
||||
|
||||
def user_mod(cursor, user, host, host_all, password, new_priv, append_privs):
|
||||
def is_hash(password):
|
||||
ishash = False
|
||||
if len(password) == 41 and password[0] == '*':
|
||||
if frozenset(password[1:]).issubset(string.hexdigits):
|
||||
ishash = True
|
||||
return ishash
|
||||
|
||||
def user_mod(cursor, user, host, host_all, password, encrypted, new_priv, append_privs):
|
||||
changed = False
|
||||
grant_option = False
|
||||
|
||||
# to simplify code, if we have a specific host and no host_all, we create
|
||||
# a list with just host and loop over that
|
||||
if host_all:
|
||||
hostnames = user_get_hostnames(cursor, user)
|
||||
else:
|
||||
hostnames = [host]
|
||||
|
||||
for host in hostnames:
|
||||
# Handle passwords
|
||||
if password is not None:
|
||||
cursor.execute("SELECT password FROM user WHERE user = %s AND host = %s", (user,host))
|
||||
# Handle clear text and hashed passwords.
|
||||
if bool(password):
|
||||
# Determine what user management method server uses
|
||||
old_user_mgmt = server_version_check(cursor)
|
||||
|
||||
if old_user_mgmt:
|
||||
cursor.execute("SELECT password FROM user WHERE user = %s AND host = %s", (user,host))
|
||||
else:
|
||||
cursor.execute("SELECT authentication_string FROM user WHERE user = %s AND host = %s", (user,host))
|
||||
current_pass_hash = cursor.fetchone()
|
||||
cursor.execute("SELECT PASSWORD(%s)", (password,))
|
||||
new_pass_hash = cursor.fetchone()
|
||||
if current_pass_hash[0] != new_pass_hash[0]:
|
||||
cursor.execute("SET PASSWORD FOR %s@%s = PASSWORD(%s)", (user,host,password))
|
||||
changed = True
|
||||
|
||||
if encrypted:
|
||||
encrypted_string = (password)
|
||||
if is_hash(password):
|
||||
if current_pass_hash[0] != encrypted_string:
|
||||
if old_user_mgmt:
|
||||
cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, password))
|
||||
else:
|
||||
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, password))
|
||||
changed = True
|
||||
else:
|
||||
module.fail_json(msg="encrypted was specified however it does not appear to be a valid hash expecting: *SHA1(SHA1(your_password))")
|
||||
else:
|
||||
if old_user_mgmt:
|
||||
cursor.execute("SELECT PASSWORD(%s)", (password,))
|
||||
else:
|
||||
cursor.execute("SELECT CONCAT('*', UCASE(SHA1(UNHEX(SHA1(%s)))))", (password,))
|
||||
new_pass_hash = cursor.fetchone()
|
||||
if current_pass_hash[0] != new_pass_hash[0]:
|
||||
if old_user_mgmt:
|
||||
cursor.execute("SET PASSWORD FOR %s@%s = PASSWORD(%s)", (user, host, password))
|
||||
else:
|
||||
cursor.execute("ALTER USER %s@%s IDENTIFIED BY %s", (user, host, password))
|
||||
changed = True
|
||||
|
||||
|
||||
# Handle privileges
|
||||
if new_priv is not None:
|
||||
curr_priv = privileges_get(cursor, user,host)
|
||||
|
||||
# If the user has privileges on a db.table that doesn't appear at all in
|
||||
# the new specification, then revoke all privileges on it.
|
||||
for db_table, priv in curr_priv.iteritems():
|
||||
# If the user has the GRANT OPTION on a db.table, revoke it first.
|
||||
if "GRANT" in priv:
|
||||
grant_option = True
|
||||
if db_table not in new_priv:
|
||||
if user != "root" and "PROXY" not in priv and not append_privs:
|
||||
privileges_revoke(cursor, user,host,db_table,priv,grant_option)
|
||||
changed = True
|
||||
|
||||
# If the user doesn't currently have any privileges on a db.table, then
|
||||
# we can perform a straight grant operation.
|
||||
for db_table, priv in new_priv.iteritems():
|
||||
if db_table not in curr_priv:
|
||||
privileges_grant(cursor, user,host,db_table,priv)
|
||||
changed = True
|
||||
|
||||
# Handle privileges
|
||||
if new_priv is not None:
|
||||
|
@ -440,6 +522,7 @@ def main():
|
|||
user=dict(required=True, aliases=['name']),
|
||||
user_anonymous=dict(type="bool", default="no"),
|
||||
password=dict(default=None, no_log=True),
|
||||
encrypted=dict(default=False, type='bool'),
|
||||
host=dict(default="localhost"),
|
||||
host_all=dict(type="bool", default="no"),
|
||||
state=dict(default="present", choices=["absent", "present"]),
|
||||
|
@ -455,6 +538,7 @@ def main():
|
|||
user = module.params["user"]
|
||||
user_anonymous = module.params["user_anonymous"]
|
||||
password = module.params["password"]
|
||||
encrypted = module.boolean(module.params["encrypted"])
|
||||
host = module.params["host"].lower()
|
||||
host_all = module.params["host_all"]
|
||||
state = module.params["state"]
|
||||
|
@ -494,9 +578,9 @@ def main():
|
|||
if user_exists(cursor, user, host, host_all):
|
||||
try:
|
||||
if update_password == 'always':
|
||||
changed = user_mod(cursor, user, host, host_all, password, priv, append_privs)
|
||||
changed = user_mod(cursor, user, host, host_all, password, encrypted, priv, append_privs)
|
||||
else:
|
||||
changed = user_mod(cursor, user, host, host_all, None, priv, append_privs)
|
||||
changed = user_mod(cursor, user, host, host_all, None, encrypted, priv, append_privs)
|
||||
|
||||
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
|
||||
module.fail_json(msg=str(e))
|
||||
|
@ -506,7 +590,7 @@ def main():
|
|||
if host_all:
|
||||
module.fail_json(msg="host_all parameter cannot be used when adding a user")
|
||||
try:
|
||||
changed = user_add(cursor, user, host, host_all, password, priv)
|
||||
changed = user_add(cursor, user, host, host_all, password, encrypted, priv)
|
||||
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
|
||||
module.fail_json(msg=str(e))
|
||||
elif state == "absent":
|
||||
|
|
25
files/acl.py
25
files/acl.py
|
@ -127,10 +127,17 @@ def split_entry(entry):
|
|||
''' splits entry and ensures normalized return'''
|
||||
|
||||
a = entry.split(':')
|
||||
|
||||
d = None
|
||||
if entry.lower().startswith("d"):
|
||||
d = True
|
||||
a.pop(0)
|
||||
|
||||
if len(a) == 2:
|
||||
a.append(None)
|
||||
|
||||
t, e, p = a
|
||||
t = t.lower()
|
||||
|
||||
if t.startswith("u"):
|
||||
t = "user"
|
||||
|
@ -143,7 +150,7 @@ def split_entry(entry):
|
|||
else:
|
||||
t = None
|
||||
|
||||
return [t, e, p]
|
||||
return [d, t, e, p]
|
||||
|
||||
|
||||
def build_entry(etype, entity, permissions=None):
|
||||
|
@ -176,9 +183,9 @@ def build_command(module, mode, path, follow, default, recursive, entry=''):
|
|||
|
||||
if default:
|
||||
if(mode == 'rm'):
|
||||
cmd.append('-k')
|
||||
cmd.insert(1, '-k')
|
||||
else: # mode == 'set' or mode == 'get'
|
||||
cmd.append('-d')
|
||||
cmd.insert(1, '-d')
|
||||
|
||||
cmd.append(path)
|
||||
return cmd
|
||||
|
@ -269,16 +276,18 @@ def main():
|
|||
if etype or entity or permissions:
|
||||
module.fail_json(msg="'entry' MUST NOT be set when 'entity', 'etype' or 'permissions' are set.")
|
||||
|
||||
if state == 'present' and entry.count(":") != 2:
|
||||
module.fail_json(msg="'entry' MUST have 3 sections divided by ':' when 'state=present'.")
|
||||
if state == 'present' and not entry.count(":") in [2, 3]:
|
||||
module.fail_json(msg="'entry' MUST have 3 or 4 sections divided by ':' when 'state=present'.")
|
||||
|
||||
if state == 'absent' and entry.count(":") != 1:
|
||||
module.fail_json(msg="'entry' MUST have 2 sections divided by ':' when 'state=absent'.")
|
||||
if state == 'absent' and not entry.count(":") in [1, 2]:
|
||||
module.fail_json(msg="'entry' MUST have 2 or 3 sections divided by ':' when 'state=absent'.")
|
||||
|
||||
if state == 'query':
|
||||
module.fail_json(msg="'entry' MUST NOT be set when 'state=query'.")
|
||||
|
||||
etype, entity, permissions = split_entry(entry)
|
||||
default_flag, etype, entity, permissions = split_entry(entry)
|
||||
if default_flag != None:
|
||||
default = default_flag
|
||||
|
||||
changed = False
|
||||
msg = ""
|
||||
|
|
|
@ -27,7 +27,7 @@ module: copy
|
|||
version_added: "historical"
|
||||
short_description: Copies files to remote locations.
|
||||
description:
|
||||
- The M(copy) module copies a file on the local box to remote locations. Use the M(fetch) module to copy files from remote locations to the local box.
|
||||
- The M(copy) module copies a file on the local box to remote locations. Use the M(fetch) module to copy files from remote locations to the local box. If you need variable interpolation in copied files, use the M(template) module.
|
||||
options:
|
||||
src:
|
||||
description:
|
||||
|
@ -310,7 +310,7 @@ def main():
|
|||
if rc != 0:
|
||||
module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc,err))
|
||||
if remote_src:
|
||||
tmpdest = tempfile.mkstemp(dir=os.basedir(dest))
|
||||
_, tmpdest = tempfile.mkstemp(dir=os.path.dirname(dest))
|
||||
shutil.copy2(src, tmpdest)
|
||||
module.atomic_move(tmpdest, dest)
|
||||
else:
|
||||
|
|
|
@ -157,8 +157,8 @@ def main():
|
|||
original_basename = dict(required=False), # Internal use only, for recursive ops
|
||||
recurse = dict(default=False, type='bool'),
|
||||
force = dict(required=False, default=False, type='bool'),
|
||||
diff_peek = dict(default=None),
|
||||
validate = dict(required=False, default=None),
|
||||
diff_peek = dict(default=None), # Internal use only, for internal checks in the action plugins
|
||||
validate = dict(required=False, default=None), # Internal use only, for template and copy
|
||||
src = dict(required=False, default=None),
|
||||
),
|
||||
add_file_common_args=True,
|
||||
|
@ -288,7 +288,7 @@ def main():
|
|||
except OSError, ex:
|
||||
# Possibly something else created the dir since the os.path.exists
|
||||
# check above. As long as it's a dir, we don't need to error out.
|
||||
if not (ex.errno == errno.EEXISTS and os.isdir(curpath)):
|
||||
if not (ex.errno == errno.EEXIST and os.isdir(curpath)):
|
||||
raise
|
||||
tmp_file_args = file_args.copy()
|
||||
tmp_file_args['path']=curpath
|
||||
|
|
|
@ -97,6 +97,23 @@ EXAMPLES = '''
|
|||
|
||||
import ConfigParser
|
||||
import sys
|
||||
import os
|
||||
|
||||
# ==============================================================
|
||||
# match_opt
|
||||
|
||||
def match_opt(option, line):
|
||||
option = re.escape(option)
|
||||
return re.match('%s *=' % option, line) \
|
||||
or re.match('# *%s *=' % option, line) \
|
||||
or re.match('; *%s *=' % option, line)
|
||||
|
||||
# ==============================================================
|
||||
# match_active_opt
|
||||
|
||||
def match_active_opt(option, line):
|
||||
option = re.escape(option)
|
||||
return re.match('%s *=' % option, line)
|
||||
|
||||
# ==============================================================
|
||||
# do_ini
|
||||
|
@ -104,6 +121,11 @@ import sys
|
|||
def do_ini(module, filename, section=None, option=None, value=None, state='present', backup=False):
|
||||
|
||||
|
||||
if not os.path.exists(filename):
|
||||
try:
|
||||
open(filename,'w').close()
|
||||
except:
|
||||
module.fail_json(msg="Destination file %s not writable" % filename)
|
||||
ini_file = open(filename, 'r')
|
||||
try:
|
||||
ini_lines = ini_file.readlines()
|
||||
|
@ -135,9 +157,7 @@ def do_ini(module, filename, section=None, option=None, value=None, state='prese
|
|||
if within_section and option:
|
||||
if state == 'present':
|
||||
# change the existing option line
|
||||
if re.match('%s *=' % option, line) \
|
||||
or re.match('# *%s *=' % option, line) \
|
||||
or re.match('; *%s *=' % option, line):
|
||||
if match_opt(option, line):
|
||||
newline = '%s = %s\n' % (option, value)
|
||||
changed = ini_lines[index] != newline
|
||||
ini_lines[index] = newline
|
||||
|
@ -148,14 +168,14 @@ def do_ini(module, filename, section=None, option=None, value=None, state='prese
|
|||
line = ini_lines[index]
|
||||
if line.startswith('['):
|
||||
break
|
||||
if re.match('%s *=' % option, line):
|
||||
if match_active_opt(option, line):
|
||||
del ini_lines[index]
|
||||
else:
|
||||
index = index + 1
|
||||
break
|
||||
else:
|
||||
# comment out the existing option line
|
||||
if re.match('%s *=' % option, line):
|
||||
if match_active_opt(option, line):
|
||||
ini_lines[index] = '#%s' % ini_lines[index]
|
||||
changed = True
|
||||
break
|
||||
|
|
|
@ -111,7 +111,7 @@ stat:
|
|||
path:
|
||||
description: The full path of the file/object to get the facts of
|
||||
returned: success and if path exists
|
||||
type: boolean
|
||||
type: string
|
||||
sample: '/path/to/file'
|
||||
mode:
|
||||
description: Unix permissions of the file in octal
|
||||
|
|
|
@ -250,7 +250,7 @@ def pick_handler(src, dest, module):
|
|||
obj = handler(src, dest, module)
|
||||
if obj.can_handle_archive():
|
||||
return obj
|
||||
module.fail_json(msg='Failed to find handler to unarchive. Make sure the required command to extract the file is installed.')
|
||||
module.fail_json(msg='Failed to find handler for "%s". Make sure the required command to extract the file is installed.' % src)
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -55,6 +55,14 @@ options:
|
|||
If C(dest) is a directory, the file will always be
|
||||
downloaded (regardless of the force option), but replaced only if the contents changed.
|
||||
required: true
|
||||
tmp_dest:
|
||||
description:
|
||||
- absolute path of where temporary file is downloaded to.
|
||||
- Defaults to TMPDIR, TEMP or TMP env variables or a platform specific value
|
||||
- https://docs.python.org/2/library/tempfile.html#tempfile.tempdir
|
||||
required: false
|
||||
default: ''
|
||||
version_added: '2.1'
|
||||
force:
|
||||
description:
|
||||
- If C(yes) and C(dest) is not a directory, will download the file every
|
||||
|
@ -175,7 +183,7 @@ def url_filename(url):
|
|||
return 'index.html'
|
||||
return fn
|
||||
|
||||
def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, headers=None):
|
||||
def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, headers=None, tmp_dest=''):
|
||||
"""
|
||||
Download data from the url and store in a temporary file.
|
||||
|
||||
|
@ -191,7 +199,19 @@ def url_get(module, url, dest, use_proxy, last_mod_time, force, timeout=10, head
|
|||
if info['status'] != 200:
|
||||
module.fail_json(msg="Request failed", status_code=info['status'], response=info['msg'], url=url, dest=dest)
|
||||
|
||||
fd, tempname = tempfile.mkstemp()
|
||||
if tmp_dest != '':
|
||||
# tmp_dest should be an existing dir
|
||||
tmp_dest_is_dir = os.path.isdir(tmp_dest)
|
||||
if not tmp_dest_is_dir:
|
||||
if os.path.exists(tmp_dest):
|
||||
module.fail_json(msg="%s is a file but should be a directory." % tmp_dest)
|
||||
else:
|
||||
module.fail_json(msg="%s directoy does not exist." % tmp_dest)
|
||||
|
||||
fd, tempname = tempfile.mkstemp(dir=tmp_dest)
|
||||
else:
|
||||
fd, tempname = tempfile.mkstemp()
|
||||
|
||||
f = os.fdopen(fd, 'wb')
|
||||
try:
|
||||
shutil.copyfileobj(rsp, f)
|
||||
|
@ -235,6 +255,7 @@ def main():
|
|||
checksum = dict(default=''),
|
||||
timeout = dict(required=False, type='int', default=10),
|
||||
headers = dict(required=False, default=None),
|
||||
tmp_dest = dict(required=False, default=''),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
|
@ -250,7 +271,8 @@ def main():
|
|||
checksum = module.params['checksum']
|
||||
use_proxy = module.params['use_proxy']
|
||||
timeout = module.params['timeout']
|
||||
|
||||
tmp_dest = os.path.expanduser(module.params['tmp_dest'])
|
||||
|
||||
# Parse headers to dict
|
||||
if module.params['headers']:
|
||||
try:
|
||||
|
@ -303,7 +325,7 @@ def main():
|
|||
last_mod_time = datetime.datetime.utcfromtimestamp(mtime)
|
||||
|
||||
# download to tmpsrc
|
||||
tmpsrc, info = url_get(module, url, dest, use_proxy, last_mod_time, force, timeout, headers)
|
||||
tmpsrc, info = url_get(module, url, dest, use_proxy, last_mod_time, force, timeout, headers, tmp_dest)
|
||||
|
||||
# Now the request has completed, we can finally generate the final
|
||||
# destination file name from the info dict.
|
||||
|
|
|
@ -159,7 +159,7 @@ EXAMPLES = '''
|
|||
register: webpage
|
||||
|
||||
- action: fail
|
||||
when: "'illustrative' not in webpage.content"
|
||||
when: "'AWESOME' not in webpage.content"
|
||||
|
||||
|
||||
# Create a JIRA issue
|
||||
|
|
|
@ -32,6 +32,7 @@ options:
|
|||
- A package name, like C(foo), or package specifier with version, like C(foo=1.0). Name wildcards (fnmatch) like C(apt*) and version wildcards like C(foo=1.0*) are also supported. Note that the apt-get commandline supports implicit regex matches here but we do not because it can let typos through easier (If you typo C(foo) as C(fo) apt-get would install packages that have "fo" in their name with a warning and a prompt for the user. Since we don't have warnings and prompts before installing we disallow this. Use an explicit fnmatch pattern if you want wildcarding)
|
||||
required: false
|
||||
default: null
|
||||
aliases: [ 'pkg', 'package' ]
|
||||
state:
|
||||
description:
|
||||
- Indicates the desired package state. C(latest) ensures that the latest version is installed. C(build-dep) ensures the package build dependencies are installed.
|
||||
|
|
|
@ -51,7 +51,7 @@ options:
|
|||
- "Package name, or package specifier with version, like C(name-1.0). When using state=latest, this can be '*' which means run: yum -y update. You can also pass a url or a local path to a rpm file. To operate on several packages this can accept a comma separated list of packages or (as of 2.0) a list of packages."
|
||||
required: true
|
||||
default: null
|
||||
aliases: []
|
||||
aliases: [ 'pkg' ]
|
||||
exclude:
|
||||
description:
|
||||
- "Package name(s) to exclude when state=present, or latest"
|
||||
|
@ -65,9 +65,9 @@ options:
|
|||
default: null
|
||||
state:
|
||||
description:
|
||||
- Whether to install (C(present), C(latest)), or remove (C(absent)) a package.
|
||||
- Whether to install (C(present) or C(installed), C(latest)), or remove (C(absent) or C(removed)) a package.
|
||||
required: false
|
||||
choices: [ "present", "latest", "absent" ]
|
||||
choices: [ "present", "installed", "latest", "absent", "removed" ]
|
||||
default: "present"
|
||||
enablerepo:
|
||||
description:
|
||||
|
@ -117,6 +117,16 @@ options:
|
|||
choices: ["yes", "no"]
|
||||
aliases: []
|
||||
|
||||
validate_certs:
|
||||
description:
|
||||
- This only applies if using a https url as the source of the rpm. e.g. for localinstall. If set to C(no), the SSL certificates will not be validated.
|
||||
- This should only set to C(no) used on personally controlled sites using self-signed certificates as it avoids verifying the source site.
|
||||
- Prior to 2.1 the code worked as if this was set to C(yes).
|
||||
required: false
|
||||
default: "yes"
|
||||
choices: ["yes", "no"]
|
||||
version_added: "2.1"
|
||||
|
||||
notes:
|
||||
- When used with a loop of package names in a playbook, ansible optimizes
|
||||
the call to the yum module. Instead of calling the module with a single
|
||||
|
@ -185,6 +195,7 @@ def yum_base(conf_file=None):
|
|||
my = yum.YumBase()
|
||||
my.preconf.debuglevel=0
|
||||
my.preconf.errorlevel=0
|
||||
my.preconf.plugins = True
|
||||
if conf_file and os.path.exists(conf_file):
|
||||
my.preconf.fn = conf_file
|
||||
if os.geteuid() != 0:
|
||||
|
@ -965,6 +976,7 @@ def main():
|
|||
conf_file=dict(default=None),
|
||||
disable_gpg_check=dict(required=False, default="no", type='bool'),
|
||||
update_cache=dict(required=False, default="no", type='bool'),
|
||||
validate_certs=dict(required=False, default="yes", type='bool'),
|
||||
# this should not be needed, but exists as a failsafe
|
||||
install_repoquery=dict(required=False, default="yes", type='bool'),
|
||||
),
|
||||
|
|
|
@ -55,7 +55,7 @@ options:
|
|||
version_added: "1.5"
|
||||
description:
|
||||
- if C(yes), adds the hostkey for the repo url if not already
|
||||
added. If ssh_args contains "-o StrictHostKeyChecking=no",
|
||||
added. If ssh_opts contains "-o StrictHostKeyChecking=no",
|
||||
this parameter is ignored.
|
||||
ssh_opts:
|
||||
required: false
|
||||
|
|
|
@ -80,6 +80,15 @@ options:
|
|||
choices: [ "yes", "no" ]
|
||||
default: "no"
|
||||
version_added: "1.9"
|
||||
validate_certs:
|
||||
description:
|
||||
- This only applies if using a https url as the source of the keys. If set to C(no), the SSL certificates will not be validated.
|
||||
- This should only set to C(no) used on personally controlled sites using self-signed certificates as it avoids verifying the source site.
|
||||
- Prior to 2.1 the code worked as if this was set to C(yes).
|
||||
required: false
|
||||
default: "yes"
|
||||
choices: ["yes", "no"]
|
||||
version_added: "2.1"
|
||||
description:
|
||||
- "Adds or removes authorized keys for particular user accounts"
|
||||
author: "Ansible Core Team"
|
||||
|
@ -93,27 +102,30 @@ EXAMPLES = '''
|
|||
- authorized_key: user=charlie key=https://github.com/charlie.keys
|
||||
|
||||
# Using alternate directory locations:
|
||||
- authorized_key: user=charlie
|
||||
key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||
path='/etc/ssh/authorized_keys/charlie'
|
||||
manage_dir=no
|
||||
- authorized_key:
|
||||
user: charlie
|
||||
key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||
path: '/etc/ssh/authorized_keys/charlie'
|
||||
manage_dir: no
|
||||
|
||||
# Using with_file
|
||||
- name: Set up authorized_keys for the deploy user
|
||||
authorized_key: user=deploy
|
||||
key="{{ item }}"
|
||||
authorized_key: user=deploy key="{{ item }}"
|
||||
with_file:
|
||||
- public_keys/doe-jane
|
||||
- public_keys/doe-john
|
||||
|
||||
# Using key_options:
|
||||
- authorized_key: user=charlie
|
||||
key="{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||
key_options='no-port-forwarding,from="10.0.1.1"'
|
||||
- authorized_key:
|
||||
user: charlie
|
||||
key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||
key_options: 'no-port-forwarding,from="10.0.1.1"'
|
||||
|
||||
# Using validate_certs:
|
||||
- authorized_key: user=charlie key=https://github.com/user.keys validate_certs=no
|
||||
|
||||
# Set up authorized_keys exclusively with one key
|
||||
- authorized_key: user=root key="{{ item }}" state=present
|
||||
exclusive=yes
|
||||
- authorized_key: user=root key="{{ item }}" state=present exclusive=yes
|
||||
with_file:
|
||||
- public_keys/doe-jane
|
||||
'''
|
||||
|
@ -358,6 +370,7 @@ def enforce_state(module, params):
|
|||
state = params.get("state", "present")
|
||||
key_options = params.get("key_options", None)
|
||||
exclusive = params.get("exclusive", False)
|
||||
validate_certs = params.get("validate_certs", True)
|
||||
error_msg = "Error getting key from: %s"
|
||||
|
||||
# if the key is a url, request it and use it as key source
|
||||
|
@ -460,6 +473,7 @@ def main():
|
|||
key_options = dict(required=False, type='str'),
|
||||
unique = dict(default=False, type='bool'),
|
||||
exclusive = dict(default=False, type='bool'),
|
||||
validate_certs = dict(default=True, type='bool'),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
|
|
@ -260,8 +260,8 @@ class SystemdStrategy(GenericStrategy):
|
|||
(rc, out, err))
|
||||
|
||||
def get_permanent_hostname(self):
|
||||
cmd = 'hostnamectl --static status'
|
||||
rc, out, err = self.module.run_command(cmd, use_unsafe_shell=True)
|
||||
cmd = ['hostnamectl', '--static', 'status']
|
||||
rc, out, err = self.module.run_command(cmd)
|
||||
if rc != 0:
|
||||
self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" %
|
||||
(rc, out, err))
|
||||
|
@ -399,6 +399,57 @@ class SolarisStrategy(GenericStrategy):
|
|||
|
||||
# ===========================================
|
||||
|
||||
class FreeBSDStrategy(GenericStrategy):
|
||||
"""
|
||||
This is a FreeBSD hostname manipulation strategy class - it edits
|
||||
the /etc/rc.conf.d/hostname file.
|
||||
"""
|
||||
|
||||
HOSTNAME_FILE = '/etc/rc.conf.d/hostname'
|
||||
|
||||
def get_permanent_hostname(self):
|
||||
|
||||
if not os.path.isfile(self.HOSTNAME_FILE):
|
||||
try:
|
||||
open(self.HOSTNAME_FILE, "a").write("hostname=temporarystub\n")
|
||||
except IOError, err:
|
||||
self.module.fail_json(msg="failed to write file: %s" %
|
||||
str(err))
|
||||
try:
|
||||
try:
|
||||
f = open(self.HOSTNAME_FILE, 'r')
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('hostname='):
|
||||
return line[10:].strip('"')
|
||||
except Exception, err:
|
||||
self.module.fail_json(msg="failed to read hostname: %s" % str(err))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
return None
|
||||
|
||||
def set_permanent_hostname(self, name):
|
||||
try:
|
||||
try:
|
||||
f = open(self.HOSTNAME_FILE, 'r')
|
||||
lines = [x.strip() for x in f]
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith('hostname='):
|
||||
lines[i] = 'hostname="%s"' % name
|
||||
break
|
||||
f.close()
|
||||
|
||||
f = open(self.HOSTNAME_FILE, 'w')
|
||||
f.write('\n'.join(lines) + '\n')
|
||||
except Exception, err:
|
||||
self.module.fail_json(msg="failed to update hostname: %s" % str(err))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
# ===========================================
|
||||
|
||||
class FedoraHostname(Hostname):
|
||||
platform = 'Linux'
|
||||
distribution = 'Fedora'
|
||||
|
@ -541,6 +592,12 @@ class SolarisHostname(Hostname):
|
|||
distribution = None
|
||||
strategy_class = SolarisStrategy
|
||||
|
||||
class FreeBSDHostname(Hostname):
|
||||
platform = 'FreeBSD'
|
||||
distribution = None
|
||||
strategy_class = FreeBSDStrategy
|
||||
|
||||
|
||||
# ===========================================
|
||||
|
||||
def main():
|
||||
|
|
|
@ -23,7 +23,7 @@ DOCUMENTATION = '''
|
|||
---
|
||||
module: ping
|
||||
version_added: historical
|
||||
short_description: Try to connect to host, veryify a usable python and return C(pong) on success.
|
||||
short_description: Try to connect to host, verify a usable python and return C(pong) on success.
|
||||
description:
|
||||
- A trivial test module, this module always returns C(pong) on successful
|
||||
contact. It does not make sense in playbooks, but it is useful from
|
||||
|
|
|
@ -49,6 +49,11 @@ options:
|
|||
- Optionally when used with the -u option, this option allows to
|
||||
change the user ID to a non-unique value.
|
||||
version_added: "1.1"
|
||||
seuser:
|
||||
required: false
|
||||
description:
|
||||
- Optionally sets the seuser type (user_u) on selinux enabled systems.
|
||||
version_added: "2.1"
|
||||
group:
|
||||
required: false
|
||||
description:
|
||||
|
@ -253,6 +258,7 @@ class User(object):
|
|||
self.name = module.params['name']
|
||||
self.uid = module.params['uid']
|
||||
self.non_unique = module.params['non_unique']
|
||||
self.seuser = module.params['seuser']
|
||||
self.group = module.params['group']
|
||||
self.groups = module.params['groups']
|
||||
self.comment = module.params['comment']
|
||||
|
@ -313,6 +319,9 @@ class User(object):
|
|||
if self.non_unique:
|
||||
cmd.append('-o')
|
||||
|
||||
if self.seuser is not None:
|
||||
cmd.append('-Z')
|
||||
cmd.append(self.seuser)
|
||||
if self.group is not None:
|
||||
if not self.group_exists(self.group):
|
||||
self.module.fail_json(msg="Group %s does not exist" % self.group)
|
||||
|
@ -1674,9 +1683,10 @@ class DarwinUser(User):
|
|||
self._update_system_user()
|
||||
# here we don't care about change status since it is a creation,
|
||||
# thus changed is always true.
|
||||
(rc, _out, _err, changed) = self._modify_group()
|
||||
out += _out
|
||||
err += _err
|
||||
if self.groups:
|
||||
(rc, _out, _err, changed) = self._modify_group()
|
||||
out += _out
|
||||
err += _err
|
||||
return (rc, err, out)
|
||||
|
||||
def modify_user(self):
|
||||
|
@ -1684,7 +1694,8 @@ class DarwinUser(User):
|
|||
out = ''
|
||||
err = ''
|
||||
|
||||
self._make_group_numerical()
|
||||
if self.group:
|
||||
self._make_group_numerical()
|
||||
|
||||
for field in self.fields:
|
||||
if self.__dict__.has_key(field[0]) and self.__dict__[field[0]]:
|
||||
|
@ -1707,12 +1718,13 @@ class DarwinUser(User):
|
|||
err += _err
|
||||
changed = rc
|
||||
|
||||
(rc, _out, _err, _changed) = self._modify_group()
|
||||
out += _out
|
||||
err += _err
|
||||
if self.groups:
|
||||
(rc, _out, _err, _changed) = self._modify_group()
|
||||
out += _out
|
||||
err += _err
|
||||
|
||||
if _changed is True:
|
||||
changed = rc
|
||||
if _changed is True:
|
||||
changed = rc
|
||||
|
||||
rc = self._update_system_user()
|
||||
if rc == 0:
|
||||
|
@ -2047,6 +2059,8 @@ def main():
|
|||
shell=dict(default=None, type='str'),
|
||||
password=dict(default=None, type='str', no_log=True),
|
||||
login_class=dict(default=None, type='str'),
|
||||
# following options are specific to selinux
|
||||
seuser=dict(default=None, type='str'),
|
||||
# following options are specific to userdel
|
||||
force=dict(default='no', type='bool'),
|
||||
remove=dict(default='no', type='bool'),
|
||||
|
|
|
@ -97,6 +97,7 @@ else:
|
|||
|
||||
apache_hashes = ["apr_md5_crypt", "des_crypt", "ldap_sha1", "plaintext"]
|
||||
|
||||
|
||||
def create_missing_directories(dest):
|
||||
destpath = os.path.dirname(dest)
|
||||
if not os.path.exists(destpath):
|
||||
|
@ -155,9 +156,6 @@ def absent(dest, username, check_mode):
|
|||
""" Ensures user is absent
|
||||
|
||||
Returns (msg, changed) """
|
||||
if not os.path.exists(dest):
|
||||
raise ValueError("%s does not exists" % dest)
|
||||
|
||||
if StrictVersion(passlib.__version__) >= StrictVersion('1.6'):
|
||||
ht = HtpasswdFile(dest, new=False)
|
||||
else:
|
||||
|
@ -244,6 +242,9 @@ def main():
|
|||
if state == 'present':
|
||||
(msg, changed) = present(path, username, password, crypt_scheme, create, check_mode)
|
||||
elif state == 'absent':
|
||||
if not os.path.exists(path):
|
||||
module.exit_json(msg="%s not present" % username,
|
||||
warnings="%s does not exist" % path, changed=False)
|
||||
(msg, changed) = absent(path, username, check_mode)
|
||||
else:
|
||||
module.fail_json(msg="Invalid state: %s" % state)
|
||||
|
|
|
@ -68,6 +68,7 @@ Set-Attr $date "year" (Get-Date -format yyyy)
|
|||
Set-Attr $date "month" (Get-Date -format MM)
|
||||
Set-Attr $date "day" (Get-Date -format dd)
|
||||
Set-Attr $date "hour" (Get-Date -format HH)
|
||||
Set-Attr $date "minute" (Get-Date -format mm)
|
||||
Set-Attr $date "iso8601" (Get-Date -format s)
|
||||
Set-Attr $result.ansible_facts "ansible_date_time" $date
|
||||
|
||||
|
|
10
windows/win_copy.py
Normal file → Executable file
10
windows/win_copy.py
Normal file → Executable file
|
@ -44,16 +44,6 @@ options:
|
|||
required: true
|
||||
default: null
|
||||
author: "Jon Hawkesworth (@jhawkesworth)"
|
||||
notes:
|
||||
- The "win_copy" module is best used for small files only.
|
||||
This module should **not** be used for files bigger than 3Mb as
|
||||
this will result in a 500 response from the winrm host
|
||||
and it will not be possible to connect via winrm again until the
|
||||
windows remote management service has been restarted on the
|
||||
windows host.
|
||||
Files larger than 1Mb will take minutes to transfer.
|
||||
The recommended way to transfer large files is using win_get_url
|
||||
or collecting from a windows file share folder.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
|
|
Loading…
Reference in a new issue