def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( suffix=dict(choices=["domain", "ca"], required=True), state=dict(type="str", default="verified", choices=["verified"]), ), supports_check_mode=True, ) ansible_module._ansible_debug = True # Get parameters suffix = ansible_module.params_get("suffix") state = ansible_module.params_get("state") # Check parameters # Init # Create command if state not in ["verified"]: ansible_module.fail_json(msg="Unkown state '%s'" % state) # Execute command with ansible_module.ipa_connect(): # Execute command ansible_module.ipa_command("topologysuffix_verify", suffix, {}) # Done ansible_module.exit_json(changed=True)
def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( # generalgroups name=dict(type="list", aliases=["cn"], default=None, required=True), description=dict(required=False, type="str", default=None), vault_type=dict(type="str", aliases=["ipavaulttype"], default=None, required=False, choices=["standard", "symmetric", "asymmetric"]), vault_public_key=dict( type="str", required=False, default=None, aliases=['ipavaultpublickey', 'public_key', 'new_public_key']), vault_public_key_file=dict( type="str", required=False, default=None, aliases=['public_key_file', 'new_public_key_file']), vault_private_key=dict( type="str", required=False, default=None, no_log=True, aliases=['ipavaultprivatekey', 'private_key']), vault_private_key_file=dict(type="str", required=False, default=None, aliases=['private_key_file']), vault_salt=dict(type="str", required=False, default=None, aliases=['ipavaultsalt', 'salt']), username=dict(type="str", required=False, default=None, aliases=['user']), service=dict(type="str", required=False, default=None), shared=dict(type="bool", required=False, default=None), users=dict(required=False, type='list', default=None), groups=dict(required=False, type='list', default=None), services=dict(required=False, type='list', default=None), owners=dict(required=False, type='list', default=None, aliases=['ownerusers']), ownergroups=dict(required=False, type='list', default=None), ownerservices=dict(required=False, type='list', default=None), vault_data=dict(type="str", required=False, default=None, no_log=True, aliases=['ipavaultdata', 'data']), datafile_in=dict(type="str", required=False, default=None, aliases=['in']), datafile_out=dict(type="str", required=False, default=None, aliases=['out']), vault_password=dict( type="str", required=False, default=None, no_log=True, aliases=['ipavaultpassword', 'password', "old_password"]), vault_password_file=dict( type="str", required=False, default=None, no_log=False, aliases=['password_file', "old_password_file"]), new_password=dict(type="str", required=False, default=None, no_log=True), new_password_file=dict(type="str", required=False, default=None, no_log=False), # state action=dict(type="str", default="vault", choices=["vault", "data", "member"]), state=dict(type="str", default="present", choices=["present", "absent", "retrieved"]), ), supports_check_mode=True, mutually_exclusive=[['username', 'service', 'shared'], ['datafile_in', 'vault_data'], ['new_password', 'new_password_file'], ['vault_password', 'vault_password_file'], ['vault_public_key', 'vault_public_key_file']], ) ansible_module._ansible_debug = True # general names = ansible_module.params_get("name") # present description = ansible_module.params_get("description") username = ansible_module.params_get("username") service = ansible_module.params_get("service") shared = ansible_module.params_get("shared") users = ansible_module.params_get("users") groups = ansible_module.params_get("groups") services = ansible_module.params_get("services") owners = ansible_module.params_get("owners") ownergroups = ansible_module.params_get("ownergroups") ownerservices = ansible_module.params_get("ownerservices") vault_type = ansible_module.params_get("vault_type") salt = ansible_module.params_get("vault_salt") password = ansible_module.params_get("vault_password") password_file = ansible_module.params_get("vault_password_file") new_password = ansible_module.params_get("new_password") new_password_file = ansible_module.params_get("new_password_file") public_key = ansible_module.params_get("vault_public_key") public_key_file = ansible_module.params_get("vault_public_key_file") private_key = ansible_module.params_get("vault_private_key") private_key_file = ansible_module.params_get("vault_private_key_file") vault_data = ansible_module.params_get("vault_data") datafile_in = ansible_module.params_get("datafile_in") datafile_out = ansible_module.params_get("datafile_out") action = ansible_module.params_get("action") state = ansible_module.params_get("state") # Check parameters if state == "present": if len(names) != 1: ansible_module.fail_json( msg="Only one vault can be added at a time.") elif state == "absent": if len(names) < 1: ansible_module.fail_json(msg="No name given.") elif state == "retrieved": if len(names) != 1: ansible_module.fail_json( msg="Only one vault can be retrieved at a time.") else: ansible_module.fail_json(msg="Invalid state '%s'" % state) check_parameters(ansible_module, state, action, description, username, service, shared, users, groups, services, owners, ownergroups, ownerservices, vault_type, salt, password, password_file, public_key, public_key_file, private_key, private_key_file, vault_data, datafile_in, datafile_out, new_password, new_password_file) # Init changed = False exit_args = {} with ansible_module.ipa_connect(context='ansible-freeipa') as ccache_name: if ccache_name is not None: os.environ["KRB5CCNAME"] = ccache_name commands = [] for name in names: # Make sure vault exists res_find = find_vault(ansible_module, name, username, service, shared) # Set default vault_type if needed. res_type = res_find.get('ipavaulttype')[0] if res_find else None if vault_type is None: vault_type = res_type if res_find is not None else u"symmetric" # Generate args args = gen_args(description, username, service, shared, vault_type, salt, public_key, public_key_file) pwdargs = None # Create command if state == "present": # verify data encription args check_encryption_params( ansible_module, state, action, vault_type, salt, password, password_file, public_key, public_key_file, private_key, private_key_file, vault_data, datafile_in, datafile_out, new_password, new_password_file, res_find) change_passwd = any([ new_password, new_password_file, (private_key or private_key_file) and (public_key or public_key_file) ]) if action == "vault": # Found the vault if res_find is not None: arg_type = args.get("ipavaulttype") modified = not compare_args_ipa( ansible_module, args, res_find) if arg_type != res_type or change_passwd: stargs = data_storage_args( res_type, args, vault_data, password, password_file, private_key, private_key_file, datafile_in, datafile_out) stored = get_stored_data(ansible_module, res_find, stargs) if stored: vault_data = \ (stored or b"").decode("utf-8") remove_attrs = { "symmetric": ["private_key", "public_key"], "asymmetric": ["password", "ipavaultsalt"], "standard": [ "private_key", "public_key", "password", "ipavaultsalt" ], } for attr in remove_attrs.get(arg_type, []): if attr in args: del args[attr] if vault_type == 'symmetric': if 'ipavaultsalt' not in args: args['ipavaultsalt'] = os.urandom(32) else: args['ipavaultsalt'] = b'' if modified: commands.append([name, "vault_mod_internal", args]) else: if vault_type == 'symmetric' \ and 'ipavaultsalt' not in args: args['ipavaultsalt'] = os.urandom(32) commands.append([name, "vault_add_internal", args]) if vault_type != 'standard' and vault_data is None: vault_data = '' # Set res_find to empty dict for next steps res_find = {} # Generate adittion and removal lists user_add, user_del = \ gen_add_del_lists(users, res_find.get('member_user', [])) group_add, group_del = \ gen_add_del_lists(groups, res_find.get('member_group', [])) service_add, service_del = \ gen_add_del_lists(services, res_find.get('member_service', [])) owner_add, owner_del = \ gen_add_del_lists(owners, res_find.get('owner_user', [])) ownergroups_add, ownergroups_del = \ gen_add_del_lists(ownergroups, res_find.get('owner_group', [])) ownerservice_add, ownerservice_del = \ gen_add_del_lists(ownerservices, res_find.get('owner_service', [])) # Add users and groups user_add_args = gen_member_args(args, user_add, group_add, service_add) if user_add_args is not None: commands.append( [name, 'vault_add_member', user_add_args]) # Remove users and groups user_del_args = gen_member_args(args, user_del, group_del, service_del) if user_del_args is not None: commands.append( [name, 'vault_remove_member', user_del_args]) # Add owner users and groups owner_add_args = gen_member_args(args, owner_add, ownergroups_add, ownerservice_add) if owner_add_args is not None: commands.append( [name, 'vault_add_owner', owner_add_args]) # Remove owner users and groups owner_del_args = gen_member_args(args, owner_del, ownergroups_del, ownerservice_del) if owner_del_args is not None: commands.append( [name, 'vault_remove_owner', owner_del_args]) elif action in "member": # Add users and groups if any([users, groups, services]): user_args = gen_member_args(args, users, groups, services) commands.append([name, 'vault_add_member', user_args]) if any([owners, ownergroups, ownerservices]): owner_args = gen_member_args(args, owners, ownergroups, ownerservices) commands.append([name, 'vault_add_owner', owner_args]) if any([vault_data, datafile_in]): if change_passwd: pwdargs = data_storage_args(vault_type, args, vault_data, new_password, new_password_file, private_key, private_key_file, datafile_in, datafile_out) else: pwdargs = data_storage_args(vault_type, args, vault_data, password, password_file, private_key, private_key_file, datafile_in, datafile_out) pwdargs['override_password'] = True pwdargs.pop("private_key", None) pwdargs.pop("private_key_file", None) commands.append([name, "vault_archive", pwdargs]) elif state == "retrieved": if res_find is None: ansible_module.fail_json( msg="Vault `%s` not found to retrieve data." % name) # verify data encription args check_encryption_params( ansible_module, state, action, vault_type, salt, password, password_file, public_key, public_key_file, private_key, private_key_file, vault_data, datafile_in, datafile_out, new_password, new_password_file, res_find) pwdargs = data_storage_args(res_find["ipavaulttype"][0], args, vault_data, password, password_file, private_key, private_key_file, datafile_in, datafile_out) if 'data' in pwdargs: del pwdargs['data'] commands.append([name, "vault_retrieve", pwdargs]) elif state == "absent": if 'ipavaulttype' in args: del args['ipavaulttype'] if action == "vault": if res_find is not None: remove = ['ipavaultsalt', 'ipavaultpublickey'] args = { k: v for k, v in args.items() if k not in remove } commands.append([name, "vault_del", args]) elif action == "member": # remove users and groups if any([users, groups, services]): user_args = gen_member_args(args, users, groups, services) commands.append( [name, 'vault_remove_member', user_args]) if any([owners, ownergroups, ownerservices]): owner_args = gen_member_args(args, owners, ownergroups, ownerservices) commands.append( [name, 'vault_remove_owner', owner_args]) else: ansible_module.fail_json( msg="Invalid action '%s' for state '%s'" % (action, state)) else: ansible_module.fail_json(msg="Unknown state '%s'" % state) # Check mode exit if ansible_module.check_mode: ansible_module.exit_json(changed=len(commands) > 0, **exit_args) # Execute commands errors = [] for name, command, args in commands: try: result = ansible_module.ipa_command(command, name, args) if command == 'vault_archive': changed = 'Archived data into' in result['summary'] elif command == 'vault_retrieve': if 'result' not in result: raise Exception("No result obtained.") if "data" in result["result"]: data_return = exit_args.setdefault("vault", {}) data_return["data"] = result["result"]["data"] else: if not datafile_out: raise Exception("No data retrieved.") changed = False else: if "completed" in result: if result["completed"] > 0: changed = True else: changed = True except ipalib_errors.EmptyModlist: result = {} except Exception as exception: ansible_module.fail_json(msg="%s: %s: %s" % (command, name, str(exception))) # Get all errors # All "already a member" and "not a member" failures in the # result are ignored. All others are reported. if "failed" in result and len(result["failed"]) > 0: for item in result["failed"]: failed_item = result["failed"][item] for member_type in failed_item: for member, failure in failed_item[member_type]: if "already a member" in failure \ or "not a member" in failure: continue errors.append( "%s: %s %s: %s" % (command, member_type, member, failure)) if len(errors) > 0: ansible_module.fail_json(msg=", ".join(errors)) # Done # exit_raw_json is a replacement for ansible_module.exit_json that # does not mask the output. exit_raw_json(ansible_module, changed=changed, **exit_args)
def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( # general name=dict(type="list", aliases=["cn"], default=None, required=True), forwarders=dict(type="list", default=None, required=False, aliases=["idnsforwarders"], elements='dict', options=dict( ip_address=dict(type='str', required=True), port=dict(type='int', required=False, default=None), )), forwardpolicy=dict(type='str', aliases=["idnsforwardpolicy", "forward_policy"], required=False, choices=['only', 'first', 'none']), skip_overlap_check=dict(type='bool', required=False), permission=dict(type='bool', required=False, aliases=['managedby']), action=dict(type="str", default="dnsforwardzone", choices=["member", "dnsforwardzone"]), # state state=dict(type='str', default='present', choices=['present', 'absent', 'enabled', 'disabled']), ), supports_check_mode=True, ) ansible_module._ansible_debug = True # Get parameters names = ansible_module.params_get("name") action = ansible_module.params_get("action") forwarders = forwarder_list( ansible_module.params_get("forwarders")) forwardpolicy = ansible_module.params_get("forwardpolicy") skip_overlap_check = ansible_module.params_get("skip_overlap_check") permission = ansible_module.params_get("permission") state = ansible_module.params_get("state") if state == 'present' and len(names) != 1: ansible_module.fail_json( msg="Only one dnsforwardzone can be added at a time.") if state == 'absent' and len(names) < 1: ansible_module.fail_json(msg="No name given.") # absent stae means delete if the action is NOT member but update if it is # if action is member then update an exisiting resource # and if action is not member then create a resource if state == "absent" and action == "dnsforwardzone": operation = "del" elif action == "member": operation = "update" else: operation = "add" invalid = [] if state in ["enabled", "disabled"]: if action == "member": ansible_module.fail_json( msg="Action `member` cannot be used with state `%s`" % (state)) invalid = [ "forwarders", "forwardpolicy", "skip_overlap_check", "permission" ] wants_enable = (state == "enabled") if operation == "del": invalid = [ "forwarders", "forwardpolicy", "skip_overlap_check", "permission" ] ansible_module.params_fail_used_invalid(invalid, state, action) changed = False exit_args = {} args = {} is_enabled = "IGNORE" # Connect to IPA API with ansible_module.ipa_connect(): # we need to determine 3 variables # args = the values we want to change/set # command = the ipa api command to call del, add, or mod # is_enabled = is the current resource enabled (True) # disabled (False) and do we care (IGNORE) for name in names: commands = [] command = None # Make sure forwardzone exists existing_resource = find_dnsforwardzone(ansible_module, name) # validate parameters if state == 'present': if existing_resource is None and not forwarders: ansible_module.fail_json(msg='No forwarders specified.') if existing_resource is None: if operation == "add": # does not exist but should be present # determine args args = gen_args(forwarders, forwardpolicy, skip_overlap_check) # set command command = "dnsforwardzone_add" # enabled or disabled? elif operation == "update": # does not exist and is updating # trying to update something that doesn't exist, so error ansible_module.fail_json( msg="dnsforwardzone '%s' not found." % (name)) elif operation == "del": # there's nothnig to do. continue else: # existing_resource is not None fix_resource_data_types(existing_resource) if state != "absent": if forwarders: forwarders = list( set(existing_resource["idnsforwarders"] + forwarders)) else: if forwarders: forwarders = list( set(existing_resource["idnsforwarders"]) - set(forwarders)) if operation == "add": # exists and should be present, has it changed? # determine args args = gen_args( forwarders, forwardpolicy, skip_overlap_check) if 'skip_overlap_check' in args: del args['skip_overlap_check'] # set command if not compare_args_ipa( ansible_module, args, existing_resource): command = "dnsforwardzone_mod" elif operation == "del": # exists but should be absent # set command command = "dnsforwardzone_del" args = {} elif operation == "update": # exists and is updating # calculate the new forwarders and mod args = gen_args( forwarders, forwardpolicy, skip_overlap_check) if "skip_overlap_check" in args: del args['skip_overlap_check'] # command if not compare_args_ipa( ansible_module, args, existing_resource): command = "dnsforwardzone_mod" if state in ['enabled', 'disabled']: if existing_resource is not None: is_enabled = existing_resource["idnszoneactive"][0] else: ansible_module.fail_json( msg="dnsforwardzone '%s' not found." % (name)) # does the enabled state match what we want (if we care) if is_enabled != "IGNORE": if wants_enable and is_enabled != "TRUE": commands.append([name, "dnsforwardzone_enable", {}]) elif not wants_enable and is_enabled != "FALSE": commands.append([name, "dnsforwardzone_disable", {}]) # if command is set... if command is not None: commands.append([name, command, args]) if permission is not None: if existing_resource is None: managedby = None else: managedby = existing_resource.get('managedby', None) if permission and managedby is None: commands.append( [name, 'dnsforwardzone_add_permission', {}] ) elif not permission and managedby is not None: commands.append( [name, 'dnsforwardzone_remove_permission', {}] ) # Check mode exit if ansible_module.check_mode: ansible_module.exit_json(changed=len(commands) > 0, **exit_args) # Execute commands for _name, command, args in commands: ansible_module.ipa_command(command, _name, args) changed = True # Done ansible_module.exit_json(changed=changed, dnsforwardzone=exit_args)
def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( # general inclusive=dict(type="list", aliases=["automemberinclusiveregex"], default=None, options=dict( key=dict(type="str", required=True), expression=dict(type="str", required=True) ), elements="dict", required=False), exclusive=dict(type="list", aliases=["automemberexclusiveregex"], default=None, options=dict( key=dict(type="str", required=True), expression=dict(type="str", required=True) ), elements="dict", required=False), name=dict(type="list", aliases=["cn"], default=None, required=False), description=dict(type="str", default=None), automember_type=dict(type='str', required=False, choices=['group', 'hostgroup']), no_wait=dict(type="bool", default=None), default_group=dict(type="str", default=None), action=dict(type="str", default="automember", choices=["member", "automember"]), state=dict(type="str", default="present", choices=["present", "absent", "rebuilt", "orphans_removed"]), users=dict(type="list", default=None), hosts=dict(type="list", default=None), ), supports_check_mode=True, ) ansible_module._ansible_debug = True # Get parameters # general names = ansible_module.params_get("name") if names is None: names = [] # present description = ansible_module.params_get("description") # conditions inclusive = ansible_module.params_get("inclusive") exclusive = ansible_module.params_get("exclusive") # no_wait for rebuilt no_wait = ansible_module.params_get("no_wait") # default_group default_group = ansible_module.params_get("default_group") # action action = ansible_module.params_get("action") # state state = ansible_module.params_get("state") # grouping/type automember_type = ansible_module.params_get("automember_type") rebuild_users = ansible_module.params_get("users") rebuild_hosts = ansible_module.params_get("hosts") # Check parameters invalid = [] if state in ["rebuilt", "orphans_removed"]: invalid = ["name", "description", "exclusive", "inclusive", "default_group"] if action == "member": ansible_module.fail_json( msg="'action=member' is not usable with state '%s'" % state) if state == "rebuilt": if automember_type == "group" and rebuild_hosts is not None: ansible_module.fail_json( msg="state %s: hosts can not be set when type is '%s'" % (state, automember_type)) if automember_type == "hostgroup" and rebuild_users is not None: ansible_module.fail_json( msg="state %s: users can not be set when type is '%s'" % (state, automember_type)) elif state == "orphans_removed": invalid.extend(["users", "hosts"]) if not automember_type: ansible_module.fail_json( msg="'automember_type' is required unless state: rebuilt") else: if default_group is not None: for param in ["name", "exclusive", "inclusive", "users", "hosts" "no_wait"]: if ansible_module.params.get(param) is not None: msg = "Cannot use {0} together with default_group" ansible_module.fail_json(msg=msg.format(param)) if action == "member": ansible_module.fail_json( msg="Cannot use default_group with action:member") if state == "absent": ansible_module.fail_json( msg="Cannot use default_group with state:absent") else: invalid = ["users", "hosts", "no_wait"] if not automember_type: ansible_module.fail_json( msg="'automember_type' is required.") ansible_module.params_fail_used_invalid(invalid, state, action) # Init changed = False exit_args = {} res_find = None with ansible_module.ipa_connect(): commands = [] for name in names: # Make sure automember rule exists res_find = find_automember(ansible_module, name, automember_type) # Check inclusive and exclusive conditions if inclusive is not None or exclusive is not None: # automember_type is either "group" or "hostgorup" if automember_type == "group": _type = u"user" elif automember_type == "hostgroup": _type = u"host" else: ansible_module.fail_json( msg="Bad automember type '%s'" % automember_type) try: aciattrs = ansible_module.ipa_command( "json_metadata", _type, {} )['objects'][_type]['aciattrs'] except Exception as ex: ansible_module.fail_json( msg="%s: %s: %s" % ("json_metadata", _type, str(ex))) check_condition_keys(ansible_module, inclusive, aciattrs) check_condition_keys(ansible_module, exclusive, aciattrs) # Create command if state == 'present': args = gen_args(description, automember_type) if action == "automember": if res_find is not None: if not compare_args_ipa(ansible_module, args, res_find, ignore=['type']): commands.append([name, 'automember_mod', args]) else: commands.append([name, 'automember_add', args]) res_find = {} if inclusive is not None: inclusive_add, inclusive_del = gen_add_del_lists( transform_conditions(inclusive), res_find.get("automemberinclusiveregex", []) ) else: inclusive_add, inclusive_del = [], [] if exclusive is not None: exclusive_add, exclusive_del = gen_add_del_lists( transform_conditions(exclusive), res_find.get("automemberexclusiveregex", []) ) else: exclusive_add, exclusive_del = [], [] elif action == "member": if res_find is None: ansible_module.fail_json( msg="No automember '%s'" % name) inclusive_add = transform_conditions(inclusive or []) inclusive_del = [] exclusive_add = transform_conditions(exclusive or []) exclusive_del = [] for _inclusive in inclusive_add: key, regex = _inclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, inclusiveregex=regex) commands.append([name, 'automember_add_condition', condition_args]) for _inclusive in inclusive_del: key, regex = _inclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, inclusiveregex=regex) commands.append([name, 'automember_remove_condition', condition_args]) for _exclusive in exclusive_add: key, regex = _exclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, exclusiveregex=regex) commands.append([name, 'automember_add_condition', condition_args]) for _exclusive in exclusive_del: key, regex = _exclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, exclusiveregex=regex) commands.append([name, 'automember_remove_condition', condition_args]) elif state == 'absent': if action == "automember": if res_find is not None: commands.append([name, 'automember_del', {'type': automember_type}]) elif action == "member": if res_find is None: ansible_module.fail_json( msg="No automember '%s'" % name) if inclusive is not None: for _inclusive in transform_conditions(inclusive): key, regex = _inclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, inclusiveregex=regex) commands.append( [name, 'automember_remove_condition', condition_args]) if exclusive is not None: for _exclusive in transform_conditions(exclusive): key, regex = _exclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, exclusiveregex=regex) commands.append([name, 'automember_remove_condition', condition_args]) if len(names) == 0: if state == "rebuilt": args = gen_rebuild_args(automember_type, rebuild_users, rebuild_hosts, no_wait) commands.append([None, 'automember_rebuild', args]) elif state == "orphans_removed": res_find = find_automember_orphans(ansible_module, automember_type) if res_find["count"] > 0: commands.append([None, 'automember_find_orphans', {'type': automember_type, 'remove': True}]) elif default_group is not None and state == "present": res_find = find_automember_default_group(ansible_module, automember_type) if default_group == "": if isinstance(res_find["automemberdefaultgroup"], list): commands.append([None, 'automember_default_group_remove', {'type': automember_type}]) else: dn_default_group = [DN(('cn', default_group), ('cn', '%ss' % automember_type), ('cn', 'accounts'), ansible_module.ipa_get_basedn())] if repr(res_find["automemberdefaultgroup"]) != \ repr(dn_default_group): commands.append( [None, 'automember_default_group_set', {'type': automember_type, 'automemberdefaultgroup': default_group}]) else: ansible_module.fail_json(msg="Invalid operation") # Execute commands changed = ansible_module.execute_ipa_commands(commands) # result["failed"] is used only for INCLUDE_RE, EXCLUDE_RE # if entries could not be added that are already there and # if entries could not be removed that are not there. # All other issues like invalid attributes etc. are handled # as exceptions. Therefore the error section is not here as # in other modules. # Done ansible_module.exit_json(changed=changed, **exit_args)
def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( # general inclusive=dict(type="list", aliases=["automemberinclusiveregex"], default=None, options=dict( key=dict(type="str", required=True), expression=dict(type="str", required=True) ), elements="dict", required=False), exclusive=dict(type="list", aliases=[ "automemberexclusiveregex"], default=None, options=dict( key=dict(type="str", required=True), expression=dict(type="str", required=True) ), elements="dict", required=False), name=dict(type="list", aliases=["cn"], default=None, required=True), description=dict(type="str", default=None), automember_type=dict(type='str', required=False, choices=['group', 'hostgroup']), action=dict(type="str", default="automember", choices=["member", "automember"]), state=dict(type="str", default="present", choices=["present", "absent", "rebuild"]), users=dict(type="list", default=None), hosts=dict(type="list", default=None), ), supports_check_mode=True, ) ansible_module._ansible_debug = True # Get parameters # general names = ansible_module.params_get("name") # present description = ansible_module.params_get("description") # conditions inclusive = ansible_module.params_get("inclusive") exclusive = ansible_module.params_get("exclusive") # action action = ansible_module.params_get("action") # state state = ansible_module.params_get("state") # grouping/type automember_type = ansible_module.params_get("automember_type") rebuild_users = ansible_module.params_get("users") rebuild_hosts = ansible_module.params_get("hosts") if (rebuild_hosts or rebuild_users) and state != "rebuild": ansible_module.fail_json( msg="'hosts' and 'users' are only valid with state: rebuild") if not automember_type and state != "rebuild": ansible_module.fail_json( msg="'automember_type' is required unless state: rebuild") # Init changed = False exit_args = {} res_find = None with ansible_module.ipa_connect(): commands = [] for name in names: # Make sure automember rule exists res_find = find_automember(ansible_module, name, automember_type) # Check inclusive and exclusive conditions if inclusive is not None or exclusive is not None: # automember_type is either "group" or "hostgorup" if automember_type == "group": _type = u"user" elif automember_type == "hostgroup": _type = u"host" else: ansible_module.fail_json( msg="Bad automember type '%s'" % automember_type) try: aciattrs = ansible_module.ipa_command( "json_metadata", _type, {} )['objects'][_type]['aciattrs'] except Exception as ex: ansible_module.fail_json( msg="%s: %s: %s" % ("json_metadata", _type, str(ex))) check_condition_keys(ansible_module, inclusive, aciattrs) check_condition_keys(ansible_module, exclusive, aciattrs) # Create command if state == 'present': args = gen_args(description, automember_type) if action == "automember": if res_find is not None: if not compare_args_ipa(ansible_module, args, res_find, ignore=['type']): commands.append([name, 'automember_mod', args]) else: commands.append([name, 'automember_add', args]) res_find = {} inclusive_add, inclusive_del = gen_add_del_lists( transform_conditions(inclusive or []), res_find.get("automemberinclusiveregex", []) ) exclusive_add, exclusive_del = gen_add_del_lists( transform_conditions(exclusive or []), res_find.get("automemberexclusiveregex", []) ) elif action == "member": if res_find is None: ansible_module.fail_json( msg="No automember '%s'" % name) inclusive_add = transform_conditions(inclusive or []) inclusive_del = [] exclusive_add = transform_conditions(exclusive or []) exclusive_del = [] for _inclusive in inclusive_add: key, regex = _inclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, inclusiveregex=regex) commands.append([name, 'automember_add_condition', condition_args]) for _inclusive in inclusive_del: key, regex = _inclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, inclusiveregex=regex) commands.append([name, 'automember_remove_condition', condition_args]) for _exclusive in exclusive_add: key, regex = _exclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, exclusiveregex=regex) commands.append([name, 'automember_add_condition', condition_args]) for _exclusive in exclusive_del: key, regex = _exclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, exclusiveregex=regex) commands.append([name, 'automember_remove_condition', condition_args]) elif state == 'absent': if action == "automember": if res_find is not None: commands.append([name, 'automember_del', {'type': automember_type}]) elif action == "member": if res_find is None: ansible_module.fail_json( msg="No automember '%s'" % name) if inclusive is not None: for _inclusive in transform_conditions(inclusive): key, regex = _inclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, inclusiveregex=regex) commands.append( [name, 'automember_remove_condition', condition_args]) if exclusive is not None: for _exclusive in transform_conditions(exclusive): key, regex = _exclusive.split("=", 1) condition_args = gen_condition_args( automember_type, key, exclusiveregex=regex) commands.append([name, 'automember_remove_condition', condition_args]) elif state == "rebuild": if automember_type: commands.append([None, 'automember_rebuild', {"type": automember_type}]) if rebuild_users: commands.append([None, 'automember_rebuild', {"users": rebuild_users}]) if rebuild_hosts: commands.append([None, 'automember_rebuild', {"hosts": rebuild_hosts}]) # Execute commands changed = ansible_module.execute_ipa_commands(commands) # result["failed"] is used only for INCLUDE_RE, EXCLUDE_RE # if entries could not be added that are already there and # if entries could not be removed that are not there. # All other issues like invalid attributes etc. are handled # as exceptions. Therefore the error section is not here as # in other modules. # Done ansible_module.exit_json(changed=changed, **exit_args)
def main(): ansible_module = IPAAnsibleModule( argument_spec=dict( suffix=dict(choices=["domain", "ca", "domain+ca"], required=True), name=dict(type="str", aliases=["cn"], default=None), left=dict(type="str", aliases=["leftnode"], default=None), right=dict(type="str", aliases=["rightnode"], default=None), direction=dict(type="str", default=None, choices=["left-to-right", "right-to-left"]), state=dict(type="str", default="present", choices=[ "present", "absent", "enabled", "disabled", "reinitialized", "checked" ]), ), supports_check_mode=True, ) ansible_module._ansible_debug = True # Get parameters suffixes = ansible_module.params_get("suffix") name = ansible_module.params_get("name") left = ansible_module.params_get("left") right = ansible_module.params_get("right") direction = ansible_module.params_get("direction") state = ansible_module.params_get("state") # Check parameters if state != "reinitialized" and direction is not None: ansible_module.fail_json( msg="Direction is not supported in this mode.") # Init changed = False exit_args = {} with ansible_module.ipa_connect(): commands = [] for suffix in suffixes.split("+"): # Create command if state in ["present", "enabled"]: # Make sure topology segment exists if left is None or right is None: ansible_module.fail_json( msg="Left and right need to be set.") args = { "iparepltoposegmentleftnode": left, "iparepltoposegmentrightnode": right, } if name is not None: args["cn"] = name res_left_right = find_left_right(ansible_module, suffix, left, right) if res_left_right is not None: if name is not None and \ res_left_right["cn"][0] != name: ansible_module.fail_json( msg="Left and right nodes already used with " "different name (cn) '%s'" % res_left_right["cn"]) # Left and right nodes and also the name can not be # changed for key in [ "iparepltoposegmentleftnode", "iparepltoposegmentrightnode" ]: if key in args: del args[key] if len(args) > 1: # cn needs to be in args always commands.append(["topologysegment_mod", args, suffix]) # else: Nothing to change else: if name is None: args["cn"] = "%s-to-%s" % (left, right) commands.append(["topologysegment_add", args, suffix]) elif state in ["absent", "disabled"]: # Make sure topology segment does not exist res_find = find_left_right_cn(ansible_module, suffix, left, right, name) if res_find is not None: # Found either given name or found name from left and right # node args = {"cn": res_find["cn"][0]} commands.append(["topologysegment_del", args, suffix]) elif state == "checked": # Check if topology segment does exists res_find = find_left_right_cn(ansible_module, suffix, left, right, name) if res_find is not None: # Found either given name or found name from left and right # node exit_args.setdefault("found", []).append(suffix) else: # Not found exit_args.setdefault("not-found", []).append(suffix) elif state == "reinitialized": # Reinitialize segment if direction not in ["left-to-right", "right-to-left"]: ansible_module.fail_json(msg="Unknown direction '%s'" % direction) res_find = find_left_right_cn(ansible_module, suffix, left, right, name) if res_find is not None: # Found either given name or found name from left and right # node args = {"cn": res_find["cn"][0]} if direction == "left-to-right": args["left"] = True elif direction == "right-to-left": args["right"] = True commands.append( ["topologysegment_reinitialize", args, suffix]) else: params = [] if name is not None: params.append("name=%s" % name) if left is not None: params.append("left=%s" % left) if right is not None: params.append("right=%s" % right) ansible_module.fail_json( msg="No entry '%s' for suffix '%s'" % (",".join(params), suffix)) else: ansible_module.fail_json(msg="Unkown state '%s'" % state) # Check mode exit if ansible_module.check_mode: ansible_module.exit_json(changed=len(commands) > 0, **exit_args) # Execute command for command, args, _suffix in commands: ansible_module.ipa_command(command, _suffix, args) changed = True # Done ansible_module.exit_json(changed=changed, **exit_args)