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'),
|
||||
|
|
|
@ -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))
|
||||
|
@ -319,8 +317,6 @@ def elb_dreg(asg_connection, module, group_name, instance_id):
|
|||
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):
|
||||
healthy_instances = []
|
||||
as_group = asg_connection.get_all_groups(names=[group_name])[0]
|
||||
|
@ -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')
|
||||
|
@ -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:
|
||||
|
@ -834,5 +828,5 @@ def main():
|
|||
changed = True
|
||||
module.exit_json( changed = changed, **asg_properties )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -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 = []
|
||||
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]
|
||||
|
||||
|
@ -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 *
|
||||
|
||||
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 *
|
||||
|
||||
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
|
||||
|
@ -274,7 +272,7 @@ def main():
|
|||
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 *
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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:
|
||||
|
|
|
@ -258,8 +258,6 @@ def boto_supports_volume_encryption():
|
|||
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')
|
||||
|
@ -397,8 +395,6 @@ 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')
|
||||
|
@ -424,7 +420,7 @@ def main():
|
|||
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")
|
||||
|
@ -489,4 +485,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -93,9 +93,6 @@ EXAMPLES = '''
|
|||
|
||||
'''
|
||||
|
||||
import time
|
||||
import sys
|
||||
|
||||
try:
|
||||
import boto
|
||||
import boto.ec2
|
||||
|
@ -219,7 +216,7 @@ def main():
|
|||
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")
|
||||
|
@ -253,7 +250,6 @@ def main():
|
|||
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
|
||||
# the attribute each time and is not used as a changed-factor.
|
||||
|
@ -294,4 +290,5 @@ def main():
|
|||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
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
|
||||
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:
|
||||
if user_exists:
|
||||
try:
|
||||
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)
|
||||
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 *
|
||||
|
||||
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:
|
||||
api_token:
|
||||
description:
|
||||
- DigitalOcean manager id.
|
||||
api_key:
|
||||
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,7 +164,7 @@ 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']
|
||||
|
||||
|
@ -175,7 +175,6 @@ def _set_tenant_id(module):
|
|||
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,18 +136,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:
|
||||
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['name'],
|
||||
|
|
|
@ -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:
|
||||
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,7 +170,7 @@ 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']
|
||||
|
||||
|
|
|
@ -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)
|
||||
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
|
||||
|
|
|
@ -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 = '''
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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,37 +258,94 @@ 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
|
||||
|
||||
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:
|
||||
# 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()
|
||||
|
||||
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
|
||||
|
@ -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)
|
||||
|
||||
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,6 +271,7 @@ 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']:
|
||||
|
@ -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,6 +1683,7 @@ 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.
|
||||
if self.groups:
|
||||
(rc, _out, _err, changed) = self._modify_group()
|
||||
out += _out
|
||||
err += _err
|
||||
|
@ -1684,6 +1694,7 @@ class DarwinUser(User):
|
|||
out = ''
|
||||
err = ''
|
||||
|
||||
if self.group:
|
||||
self._make_group_numerical()
|
||||
|
||||
for field in self.fields:
|
||||
|
@ -1707,6 +1718,7 @@ class DarwinUser(User):
|
|||
err += _err
|
||||
changed = rc
|
||||
|
||||
if self.groups:
|
||||
(rc, _out, _err, _changed) = self._modify_group()
|
||||
out += _out
|
||||
err += _err
|
||||
|
@ -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